浅谈mock测试

浅谈mock测试

mock即模拟,用来替代一些真实的操作或者功能,个人觉得mock在单测中的最大用处就是替代被测试目标中的外部依赖,比如数据库查询或者restful api请求等。java和python中都有mock库,本文以python为例,python3.3以上版本标准库自带unittest.mock,其他版本则需要安装mock扩展:pip install mock。先来看一个简单的例子:

'''demo.py 包含被测试的代码,列出了两种类型:一个是函数式的样例,一个是OOP样例'''

def get_data():
    '''假设该函数用来请求外部http接口获取数据'''
    pass

def demo_func():
    '''被测试函数'''
    data = get_data()
    return data + 1

class DemoClass:

    def _get_data(self):
        '''从http接口获取数据,假设返回值是整数'''
        pass

    def handle_data(self):
        '''被测试方法'''
        data = self._get_data()
        return data + 1
import unittest
from unittest.mock import Mock, patch

import demo
from demo import DemoClass


class DemoTest(unittest.TestCase):
    '''mock测试用例'''
    @classmethod
    def setUpClass(cls):
        cls.demo = DemoClass()
    
    @classmethod
    def tearDownClass(cls):
        pass
        
    def test_mock_handle_data01(self):
        self.demo._get_data = Mock()
        self.demo._get_data.return_value = 999
        self.assertEqual(1000, self.demo.handle_data())

    #被patch的对象路径必须是可以被import的
    @patch('demo.DemoClass._get_data')
    def test_mock_handle_data02(self, mock_get_data):
        '''和上边的用例一样,只是实现方式不同'''
        mock_get_data.return_value = 999
        self.assertEqual(1000, self.demo.handle_data())

    def test_demo_func01(self):
        demo.get_data = Mock(return_value=887)
        self.assertEqual(888, demo.demo_func())
        
    @patch('demo.get_data')
    def test_demo_func02(self, mock_data):
        mock_data.return_value = 887
        self.assertEqual(888, demo.demo_func())
        

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

从例子上可以看到,所有需要通过请求http接口获取的数据都被mock替换了,虽然就是这样一个简单的操作,但是不可小觑。我们来简单总结一下:

1、既然是外部依赖,肯定是本地无法修改的,而且通常外部接口返回的数据都不是固定的,从而导致被测试的目标函数或方法的返回值是动态的(不包括布尔返回值),这种情况下,我们是无法在单测中使用断言的,因为断言的前提是有预期值。因为外部接口返回值的数据结构都是一定的,这时候就体现出mock的最大价值,可以将外部返回的数据替换为相应结构的常量,就可以做断言判断了。简而言之就是,解耦外部依赖,将不稳定的模拟为稳定的。

2、替换之后,在测试过程中就不需要发起真实的请求了,这样也减轻了外部服务的压力,节省了资源,可能不是很多,省一点是一点嘛;再则就是,如果外部服务维护停机或是宕机了,也不会影响单测用例的执行,可谓是风一样的用例~~

上边列举的是mock在单测中的简单示例,在实际的应用测试中也可以结合mock进行测试,比如测试一个商品页面,如果要测试存在大量商品时的分页的页面展示,需要后台创建很多测试数据,而造测试数据通常是很费劲的,尤其是比较大型的应用。此时就可以借助mock来实现模拟返回,但是通过上述示例所描述的mock模块是无法实现的,这时候需要借助mock-server了,搭建一个mock-server作为代理服务器,创建匹配规则与测试数据,从客户端或web端发起的所有请求都会经过mock-server,符合匹配规则的请求则会返回设置好的数据,而不会再请求真实的服务端;没有命中匹配规则的则由mock-server转发给服务端去处理。大概流程如下图所示:

通过在用户端与服务端增加一个mock-server代理,来处理需要测试的请求,十分灵活,而且通过这种方式可以覆盖多种测试场景,可以大大提升自动化测试的覆盖率。这里只提供一个大体的思路,具体的实现可以借助开源工具,这里推荐两个WireMockMockServer

关于python mock的详细使用教程可参考官方文档使用示例,java可参考mockito的官方文档

 

Comments are closed.