如何在python中为全部测试全局模拟一个方法

时间:2016-02-23 19:33:49

标签: python django unit-testing mocking

我的Django项目在多个应用程序的各种模块中进行了数百次测试。最近我们添加了一个功能,在创建User对象(使用Django Signals)时发送电子邮件(通过sendgrid)。

我们遇到的问题是,在运行测试时,许多用户都是显式创建的,或者是作为fixtures创建的。这导致每个测试周期发送数百封电子邮件,并且因为大多数电子邮件无效,我们会获得数百次反弹。除了所涉及的费用之外,由于奇怪的行为,Sendgrid实际上暂时暂停了我们的帐户。

显然我可以单独模拟每个测试的调用,但这必须在数百个地方进行,我们必须记住在我们创建的所有未来测试中都这样做。

是否有一种更简单的方法可以全局模拟所有测试的特定代码块(当然实际运行时保持原样)

3 个答案:

答案 0 :(得分:2)

我不使用Django,也许有一些惯用的方法可以在Django中做得很好。

我遇到此类问题的方法是创建自己的TestCase课程,该课程从unittest.TestCase延伸并覆盖setUpClass() / tearDownClass / setUp() / { {1}}在我的测试中(或者至少在其中的一部分中)设置全局需要的模拟/补丁。

现在每次我需要它而不是导入tearDown()模块我导入unittest.TestCase

示例:myunittest.TestCase

myunittest.py

在你的测试中:

import unittest

class TestCase(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        super(TestCase, cls).setUpClass()
        # Init your class Mock/Patch

    @classmethod
    def tearDownClass(cls):
        # Remove Mocks or clean your singletons
        super(TestCase, cls).tearDownClass()

    def setUp(self):
        super(TestCase, self).setUp()
        # Init your obj Mock/Patch

    @classmethod
    def tearDown(self):
        # ... if you need it
        super(TestCase, self).tearDown()

答案 1 :(得分:1)

以下是两种实现模拟的方法。

方法1:修改生产代码:

您可以创建伪包并将其导入以进行测试,而不是导入原始包。这种基于检查的导入可以在每个文件的开头完成。

例如:

import os
    if 'TEST' in os.environ:
        import pseudoTime as time
    else:
        import time

print time.time

方法2:不修改生产代码:

在测试程序中,您可以导入实用程序包(包含问题中描述的电子邮件功能的程序包)并覆盖实用程序功能。

例如:

请考虑以下代码:

import time
def function():
    return time.time()

测试代码可以执行以下操作:

import code
import time

def helloWorld():
    return "Hello World"

print "Before changing ...", code.function()
oldTime = time.time # save
time.time = helloWorld
print "After changing ...", code.function()
time.time = oldTime # revert back

上述测试的输出是:

Before changing ... 1456487043.76
After changing ... Hello World

因此,测试代码可以导入实用程序文件,覆盖它提供的功能,然后对生产代码运行测试。

此方法优越,因为它不会更改生产代码。

答案 2 :(得分:0)

在大型Django项目中使用的两种方法

假设一个:my_mock = patch("myapp.mymodule.MyClass.my_method")

1)您可以在自定义测试运行器类中添加一个模拟:

from mock import patch
from django.test.runner import DiscoverRunner

class MyTestRunner(DiscoverRunner):
    @my_mock
    def run_tests(self, test_labels, **kwargs):
         return super(MyTestRunner, self).run_tests(test_labels, **kwargs)

2)您可以在自定义基础测试类上添加模拟:

from mock import patch
from django.test import TestCase

class MyTestCase(TestCase):

   def setUp(self):
      super(MyTestCase, self).setUp()
      my_mock.start()

   def tearDown(self):
      super(MyTestCase, self).tearDown()
      my_mock.stop()