【问题标题】:Python how to reuse a Mock to avoid writing mock.patch multiple times?Python如何重用一个Mock以避免多次编写mock.patch?
【发布时间】:2019-07-31 23:20:48
【问题描述】:

给定代码如下:

import flask
import time

app = flask.Flask(__name__)

def authorize():
    print('starting authorize io')
    time.sleep(1)
    print('done authorize io')

class BlockingIo():
    def __init__(self, n):
        self.n = n
    def do(self):
        print('starting blocking io')
        time.sleep(1)
        print('ending blocking io')

@app.route('/', methods=['GET'])
@app.route('/<int:n>/', methods=['GET'])
def foo(n=1):
    authorize()
    b = BlockingIo(n)
    b.do()
    return str(n), 200

#app.run(port=5000)

我希望能够为GET /n/ 编写几个测试,每个测试都模拟authorize 和BlockingIO(n):

app.testing = True
testapp = app.test_client()

import unittest
from unittest import mock

mock.patch('__main__.authorize')

class TestBlockingIo(unittest.TestCase):
    @mock.patch('__main__.authorize')
    @mock.patch('__main__.BlockingIo.do')
    def test_1(self, m, m2):
        r = testapp.get('/1/')
        self.assertEquals(r.data, b'1')
    @mock.patch('__main__.authorize')
    @mock.patch('__main__.BlockingIo.do')
    def test_2(self, m, m2):
        r = testapp.get('/2/')
        self.assertEquals(r.data, b'2')

unittest.main()

但是,我不想一遍又一遍地写出@mock.patch装饰器。

我知道我们可以使用类装饰器,并且我可以子类化以获得更多的可重用性:

@mock.patch('__main__.authorize')
@mock.patch('__main__.BlockingIo.do')
class TestBlockingIo(unittest.TestCase):
    def test_1(self, m, m2):
        r = testapp.get('/1/')
        self.assertEquals(r.data, b'1')
    def test_2(self, m, m2):
        r = testapp.get('/2/')
        self.assertEquals(r.data, b'2')

但是,这会强制类中的所有测试函数为每个模拟采用一个额外的参数。如果我在这个类中有不需要模拟 BlockingIo 或 authorize 的测试怎么办?

我想我想要的是一种方法来执行以下操作:

m = mock.something('__main__.authorize')
m2 = mock.something('__main__.BlockingIo.do')    

class TestBlockingIo(unittest.TestCase):
    def test_1(self):
        r = testapp.get('/1/')
        self.assertEquals(r.data, b'1')
    def test_2(self):
        r = testapp.get('/2/')
        self.assertEquals(r.data, b'2')

如何重复使用我的 @mock.patch('__main__.authorize') 和 @mock.patch('__main__.BlockingIo.do') 以避免重复自己通过测试?

【问题讨论】:

    标签: python python-3.x flask mocking python-mock


    【解决方案1】:

    您可以使用patches 并在setUp 块中重复使用它们。

    补丁很不错,因为您可以在完成测试后“取消补丁”,这意味着您不会永远嘲笑事物,因为其他一些测试可能需要在真实代码上运行。

    在上面的链接中,您将看到以下代码:

    >>> class MyTest(TestCase):
    ...     def setUp(self):
    ...         patcher = patch('package.module.Class')
    ...         self.MockClass = patcher.start()
    ...         self.addCleanup(patcher.stop)
    ...
    ...     def test_something(self):
    ...         assert package.module.Class is self.MockClass
    ...
    

    它工作正常,但我真的不喜欢为每个补丁调用patch()、start() 和addCleanup()。

    您可以轻松地将其纳入可以在测试类中重用的基类中:

    class PatchMixin:
    
        def patch(self, target, **kwargs):
            p = mock.patch(target, **kwargs)
            p.start()
            self.addCleanup(p.stop)
    
    class TestBlockingIo(unittest.TestCase, PatchMixin):
    
        def setUp(self):
            self.patch('__main__.authorize')
            self.patch('__main__.BlockingIo.do')
    
        def test_1(self):
            r = testapp.get('/1/')
            self.assertEquals(r.data, b'1')
    
        def test_2(self):
            r = testapp.get('/2/')
            self.assertEquals(r.data, b'2')
    

    【讨论】:

    • 如果你想在测试中配置模拟,存储并从patch返回ps.start()。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-19
    相关资源
    最近更新 更多