how to mock data in python unittest


  1. Introduction

What is mock?

mock translated to mean simulation. The mock presented here is a module that aids in unit testing. It allows you to replace parts of your system with mock objects and make assertions about the way they have been used.

When to use mock?

Scenario 1: For example, there are two modules A and B. Module A has methods that call module B, but the methods in module B that are called by module A need to be modified for some reasons, yet we don’t want to affect the functionality of the module A. This is when you use a mock, which is used to simulate a dummy B module.

Scenario 2: Sometimes you need to prepare some other resources for the initial setup of the unit test, but these resources are not too often used or used more trouble, at this time we can define a mock object to simulate a need to use resources, used in place of the test to prepare resources.

Installation

2.1 Python 3.3 or before
Before Python 3.3, mock is a separate module and needs to be installed separately.

There are 2 ways to install it (either one will do).

1、Install using pip

Type pip install mock on the command line.

2、Source code installation

Download URL: https://pypi.org/project/mock/

Translated with DeepL.com (free version)

Open the command line, jump to the path of the extracted directory, and enter the install command python setup.py install.

After that, you can use mock in your code by importing it directly.

import mock

2.2 Python 3.3 and above
In Python 3.3 and higher, mock has been integrated into the unittest unit testing framework, so you can use it directly.

You can use mock by importing it directly into your code.

from unittest import mock

3, the basic example

Mock object is the mock module in the most important concepts. mock object is a mock module in a class instance, this class instance can be used to replace other Python objects to achieve the effect of simulation.

General usage of Mock object:

Step 1: Find the object you want to replace (a class, or a function, or a class instance).

Step 2: Instantiate the Mock class to get a mock object, and set the behavior of this mock object (e.g. what value is returned when called, what value is returned when a member is accessed, etc.).

Step 3: Use this mock object to replace the object we want to replace, that is, the object identified in step 1.

Step 4: Then you can start writing test cases, this time we can ensure that we replace the object in the test case during the execution of the same behavior as we preset.

Here begins to explain the basic example:

First, create Demo.py file.

Create the function under test (get, send_request)

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
基本示例:被测试函数
"""
# 引入Requests库
import requests

def get(url):
    # 发起GET请求
    r = requests.get(url)
    # 返回状态码
    return r.status_code

def send_request():
    # 调用get()函数
    return get('https://www.baidu.com/')

Second, create the MockTest.py file.

Create the TestDemo test class.

1, do not use mock

1.1, script code:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
基本示例:测试类
"""
import unittest
from demo import Demo

class TestDemo(unittest.TestCase):

    def test_request(self):
        print(Demo.send_request())
        self.assertEqual(Demo.send_request(), 200)

if __name__ == '__main__':
    unittest.main(verbosity=2)

1.2. Execute the MockTest.py file and run the results:

Call Demo.send_request(), expecting the same value as the actual status code return.

2、Use mock

2.1, script code:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
基本示例:测试类
"""
import unittest
from unittest import mock
from demo import Demo

class TestDemo(unittest.TestCase):

    def test_request_mock(self):
        Demo.get = mock.Mock(return_value=404)
        print(Demo.get())
        self.assertEqual(Demo.send_request(),404)

if __name__ == '__main__':
    unittest.main(verbosity=2)

2.2. Execute the MockTest.py file and run the results:

(1) first instantiate the Mock class to get a mock object, and set the behavior of this mock object (return value of 404).

(2) Use this mock object to replace the object we want to replace (Demo.get).

(3) Call Demo.send_request() expecting the same value as the preset (404).

4, constructor

4.1 Name
name: is the unique identity of the mock object; used to name a mock object, just play the role of identification, when you print a mock object, you can see its name.

1, create MockTest_name.py file.

Script code:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
构造器:参数name
"""
from unittest import mock
# name定义了mock对象的唯一标示符
testMock = mock.Mock(name='MyMock')
print(testMock)
  1. Execute the MockTest_name.py file and run the results:

Define the mock object uniquely identified as ‘MyMock’.

4.2. spec
spec: set the properties of the mock object, either properties or methods, or other list strings or other python classes.

1, create MockTest_spec.py file.

Script code:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
构造器:参数spec
"""
from unittest import mock
# 指定属性组成的list
MyList = ['username','password']
# spec设置mock对象的属性,可以是property或者方法;属性可以是一个列表字符串或者是其他的Python类
testMock = mock.Mock(spec=MyList)
print(testMock)
print(testMock.username)
print(testMock.password)
  1. Execute the MockTest_spec.py file and run the results:

Set mock two attributes (username, password)

4.3. return_value
return_value: set the value of return_value to be displayed when the mock object is called.

Create Demo.py file (create the class under test: People class).

Script code:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
被测试类
"""
# People类里有两个成员方法(一个有参数,一个无参数)、一个静态方法
class People:

    def __init__(self):
        self.__age = 20

    def name(self,firstName,lastName):
        return firstName + ' ' + lastName

    def age(self):
        return self.__age

    @staticmethod
    def class_name():
        return People.__name__

4.3.1 Specifying a value
Create the MockTest_return_value.py file (to create the PeopleTest test class).

1, do not use mock

1.1, script code:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
构造器:参数return_value(返回固定值)
"""
from method.Demo import People
import unittest

class PeopleTest(unittest.TestCase):

    def test_age(self):
        # 调用被测试类People()
        p = People()
        print(p.age())
        self.assertEqual(p.age(), 20)

if __name__ == '__main__':
    unittest.main(verbosity=2)

1.2. Execute the MockTest_return_value.py file and run the results:

Without mock, p.age() returns 20.

2、Use mock

2.1, script code:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
常用方法:构造器,参数return_value(返回固定值)
"""
from method.Demo import People
from unittest import mock
import unittest

class PeopleTest(unittest.TestCase):

    # 指定某个值
    def test_age(self):
        # 调用被测试类People()
        p = People()
        p.age = mock.Mock(return_value=50)
        print(p.age())
        self.assertEqual(p.age(), 50)

    # 指定某个值
    def test_name(self):
        # 调用被测试类People()
        p = People()
        p.name = mock.Mock(return_value='Hello Mock')
        print(p.name())
        self.assertEqual(p.name(), 'Hello Mock')

if __name__ == '__main__':
    unittest.main(verbosity=2)

2.2. Execute the MockTest_return_value.py file and run the results:

(1) test_age method, mock off the age method, let it return 50.

(2) test_name method, mock off the name method, let it return Hello Mock.

4.3.2 Specifying a class object
1, create MockTest_return_value.py file (create PeopleTest test class).

Script code:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
构造器:参数return_value(返回固定值)
"""
from method.Demo import People
from unittest import mock
import unittest

class PeopleTest(unittest.TestCase):

    # 指定某个类对象
    def test_People(self):
        testMock = mock.Mock(return_value=People())
        print(testMock)
        print(testMock().class_name())
        print(testMock().name('Hello','Mock'))
        print(testMock().age())

if __name__ == '__main__':
    unittest.main(verbosity=2)
  1. Execute the MockTest_return_value.py file and run the results:

Specify the People class and execute the methods in the class.

4.4. side_effect
side_effect: and return_value is the opposite of return_value, overrides the return_value, that is to say, when this mock object is called, the return is the value of side_effect, not return_value.

Create the Demo.py file (create the class under test: People class).

Script code:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
被测试类
"""
# People类里有两个成员方法(一个有参数,一个无参数)、一个静态方法
class People:

    def __init__(self):
        self.__age = 20

    def name(self,firstName,lastName):
        return firstName + ' ' + lastName

    def age(self):
        return self.__age

    @staticmethod
    def class_name():
        return People.__name__

4.4.1, return the specified values in turn
1, create MockTest_side_effect.py file (create PeopleTest test class).

Script code:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
构造器:参数side_effect(它给mock分配了可替换的结果,覆盖了return_value)
"""
from method.Demo import People
from unittest import mock
import unittest

class PeopleTest(unittest.TestCase):

    # 依次返回指定值
    def test_age(self):
        # 调用被测试类People()
        p = People()
        MyList = [15,16,17]
        p.age = mock.Mock(return_value=50,side_effect=MyList)
        print(p.age())
        print(p.age())
        print(p.age())
        print(p.age())

if __name__ == '__main__':
    unittest.main(verbosity=2)
  1. Execute the MockTest_side_effect.py file and run the results:

(1) Executing the first 3 p.age() returns a value, i.e. 15, 16, 17.

(2) When the 4th p.age() is executed, because the return value is not available, then an exception is thrown.

4.4.2, according to the parameters to return the specified value
1, create MockTest_side_effect.py file (create PeopleTest test class).

Script code:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
构造器:参数side_effect(它给mock分配了可替换的结果,覆盖了return_value)
"""
from method.Demo import People
from unittest import mock
import unittest

class PeopleTest(unittest.TestCase):

    # 根据参数返回指定值
    def test_name(self):
        # 调用被测试类People()
        p = People()
        values = {('a', 'b'): 'Hello Mock', ('1', '2'): 'Hello World'}
        p.name = mock.Mock(side_effect=lambda x, y: values[(x, y)])
        print(p.name('a', 'b'))
        print(p.name('1', '2'))
        self.assertEqual(p.name('a', 'b'), 'Hello Mock')
        self.assertEqual(p.name('1', '2'), 'Hello World') 

if __name__ == '__main__':
    unittest.main(verbosity=2)
  1. Execute the MockTest_side_effect.py file and run the results:

(1) Execute p.name(‘a’, ‘b’) method corresponding to the value of Hello Mock.

(2) Execute p.name(‘1’, ‘2’) method corresponding to the value Hello World.

4.4.3 Throwing Exceptions
1, create MockTest_side_effect.py file (create PeopleTest test class).

Script code:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
构造器:参数side_effect(它给mock分配了可替换的结果,覆盖了return_value)
"""
from method.Demo import People
from unittest import mock
import unittest

class PeopleTest(unittest.TestCase):

    # 抛出异常
    def test_age_error(self):
        # 调用被测试类People()
        p = People()
        p.age = mock.Mock(side_effect=SystemError)
        print(p.age())

if __name__ == '__main__':
    unittest.main(verbosity=2)
  1. Execute the MockTest_side_effect.py file and run the results:

Set side_effect to SystemError exception, call p.age() method will throw the exception.


文章作者: 倪春恩
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 倪春恩 !