如何全局模拟python中所有测试的方法

如何全局模拟python中所有测试的方法,python,django,unit-testing,mocking,Python,Django,Unit Testing,Mocking,我的Django项目在许多应用程序的不同模块中进行了数百次测试。最近,我们添加了一个功能,在创建用户对象时(使用Django信号)发送电子邮件(通过sendgrid) 我们遇到的问题是,在运行测试时,许多用户要么显式创建,要么作为设备创建。这会导致在每个测试周期中发送数百封电子邮件,并且因为其中大多数邮件无效,我们会收到数百封回复。除了所涉及的成本,Sendgrid实际上由于这种奇怪的行为暂时中止了我们的账户 显然,我可以单独模拟每个测试的调用,但是这必须在数百个地方进行,并且我们必须记住在我们

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

我们遇到的问题是,在运行测试时,许多用户要么显式创建,要么作为设备创建。这会导致在每个测试周期中发送数百封电子邮件,并且因为其中大多数邮件无效,我们会收到数百封回复。除了所涉及的成本,Sendgrid实际上由于这种奇怪的行为暂时中止了我们的账户

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


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

下面是两种实现模拟的方法

方法1:修改生产代码:

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

例如:

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

print time.time
方法2:不修改生产代码:

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

例如:

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

print time.time
考虑以下代码:

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
因此,测试代码可以导入实用程序文件,覆盖它提供的函数,然后在生产代码上运行测试


这种方法非常优越,因为它不会更改生产代码。

我不使用Django,也许有一些惯用的方法可以在Django中很好地完成它

我解决这类问题的方法是创建我自己的
TestCase
类,该类从
unittest.TestCase
扩展,并覆盖
setUpClass()
/
tearDownClass
/
setUp()
设置我在测试中全局(或至少部分测试中)需要的模拟/补丁

现在每次我需要它而不是导入
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()
在你的测试中:

from myunittest import TestCase

    class Test(TestCase):
        ... Your test

我在一个大型django项目中使用了两种方法

假设一个:
mymock=patch(“myapp.mymodule.MyClass.myu方法”)

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()

把它放在每个生产代码文件的开头?我讨厌它!说得对。编辑我的答案以提供其他方法。避免全局修补。看一看示例代码片段,它们是为了说明模拟测试的基本思想。