我如何为 python 中的所有测试全局模拟一个方法
How can I mock a method globally for all tests in python
我的 Django 项目在多个应用程序的各个模块中进行了数百次测试。最近我们添加了一个功能,在创建用户对象(使用 Django Signals)时发送电子邮件(通过 sendgrid)。
我们 运行 遇到的问题是,当 运行 测试时,许多用户是显式创建的或作为固定装置创建的。这导致每个测试周期发送数百封电子邮件,并且由于其中大部分无效,我们收到数百次退回邮件。除了所涉及的费用之外,Sendgrid 实际上还因为奇怪的行为而暂时暂停了我们的帐户。
显然我可以分别模拟每个测试的调用,但是这必须在数百个地方进行,我们必须记住在我们创建的所有未来测试中都这样做。
有没有更简单的方法来全局模拟所有测试的特定代码块(当然,在实际 运行 时保持完整)
下面是实现模拟的两种方法。
方法一:修改生产代码:
您可以创建一个伪包并导入它进行测试,而不是导入原始包。这种基于检查的导入可以在每个文件的开头完成。
例如:
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
class,它从 unittest.TestCase
扩展并覆盖 setUpClass()
/tearDownClass
/setUp()
/tearDown()
在我的测试中全局设置我需要的 mock/patch(或至少在其中的一部分)。
现在每次我需要它而不是导入 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大项目中用过的两种方式
假设一个:my_mock = patch("myapp.mymodule.MyClass.my_method")
1) 您可以在自定义测试运行程序中添加模拟 class:
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) 您可以在自定义基础测试上添加模拟 class:
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()
我的 Django 项目在多个应用程序的各个模块中进行了数百次测试。最近我们添加了一个功能,在创建用户对象(使用 Django Signals)时发送电子邮件(通过 sendgrid)。
我们 运行 遇到的问题是,当 运行 测试时,许多用户是显式创建的或作为固定装置创建的。这导致每个测试周期发送数百封电子邮件,并且由于其中大部分无效,我们收到数百次退回邮件。除了所涉及的费用之外,Sendgrid 实际上还因为奇怪的行为而暂时暂停了我们的帐户。
显然我可以分别模拟每个测试的调用,但是这必须在数百个地方进行,我们必须记住在我们创建的所有未来测试中都这样做。
有没有更简单的方法来全局模拟所有测试的特定代码块(当然,在实际 运行 时保持完整)
下面是实现模拟的两种方法。
方法一:修改生产代码:
您可以创建一个伪包并导入它进行测试,而不是导入原始包。这种基于检查的导入可以在每个文件的开头完成。
例如:
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
class,它从 unittest.TestCase
扩展并覆盖 setUpClass()
/tearDownClass
/setUp()
/tearDown()
在我的测试中全局设置我需要的 mock/patch(或至少在其中的一部分)。
现在每次我需要它而不是导入 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大项目中用过的两种方式
假设一个:my_mock = patch("myapp.mymodule.MyClass.my_method")
1) 您可以在自定义测试运行程序中添加模拟 class:
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) 您可以在自定义基础测试上添加模拟 class:
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()