【发布时间】:2017-09-07 14:47:53
【问题描述】:
我需要用不同的参数测试一个函数,最合适的方法似乎是使用with self.subTest(...) 上下文管理器。
但是,该函数向 db 写入了一些内容,并最终处于不一致的状态。我可以删除我写的东西,但是如果我可以完全重新创建整个数据库会更干净。有没有办法做到这一点?
【问题讨论】:
我需要用不同的参数测试一个函数,最合适的方法似乎是使用with self.subTest(...) 上下文管理器。
但是,该函数向 db 写入了一些内容,并最终处于不一致的状态。我可以删除我写的东西,但是如果我可以完全重新创建整个数据库会更干净。有没有办法做到这一点?
【问题讨论】:
不确定如何在 self.subTest() 中重新创建数据库,但我目前正在使用另一种技术,您可能会感兴趣。您可以使用固定装置创建数据库的“快照”,该快照基本上将复制到仅用于测试目的的第二个数据库中。我目前使用这种方法来测试我正在工作的一个大项目的代码。
我将发布一些示例代码,让您了解这在实践中会是什么样子,但您可能需要做一些额外的研究来根据您的需要定制代码(我添加了一些链接来指导您) .
这个过程相当简单。您将创建一个数据库副本,其中仅包含使用夹具所需的数据,这些数据将存储在 .yaml 文件中,并且只能由您的测试单元访问。
下面是这个过程的样子:
generate.py
django.setup()
stdout = sys.stdout
conf = [
{
'file': 'myfile.yaml',
'models': [
dict(model='your.model', pks='your, primary, keys'),
dict(model='your.model', pks='your, primary, keys')
]
}
]
for fixture in conf:
print('Processing: %s' % fixture['file'])
with open(fixture['file'], 'w') as f:
sys.stdout = FixtureAnonymiser(f)
for model in fixture['models']:
call_command('dumpdata', model.pop('model'), format='yaml',indent=4, **model)
sys.stdout.flush()
sys.stdout = stdout
test_class.py
from django.test import TestCase
class classTest(TestCase):
fixtures = ('myfile.yaml',)
def setUp(self):
"""setup tests cases"""
# create the object you want to test here, which will use data from the fixtures
def test_function(self):
self.assertEqual(True,True)
# write your test here
您可以在这里阅读更多内容:
如果您有任何问题,因为不清楚,请尽管问,我很乐意为您提供帮助。
【讨论】:
setup_db 函数并在我的测试函数顶部调用它。另外我使用faker 包来生成假数据。到目前为止,这种方法对我来说效果最好。
也许我的解决方案会对某人有所帮助
我使用事务回滚到我在测试开始时的数据库状态。
我使用 Eric Cousineau 的 decorator function 来参数化测试
更多关于数据库事务在 django documentation page
import functools
from django.db import transaction
from django.test import TransactionTestCase
from django.contrib.auth import get_user_model
User = get_user_model()
def sub_test(param_list):
"""Decorates a test case to run it as a set of subtests."""
def decorator(f):
@functools.wraps(f)
def wrapped(self):
for param in param_list:
with self.subTest(**param):
f(self, **param)
return wrapped
return decorator
class MyTestCase(TransactionTestCase):
@sub_test([
dict(email="new@user.com", password='12345678'),
dict(email="new@user.com", password='password'),
])
def test_passwords(self, email, password):
# open a transaction
with transaction.atomic():
# Creates a new savepoint. Returns the savepoint ID (sid).
sid = transaction.savepoint()
# create user and check, if there only one with this email in DB
user = User.objects.create(email=email, password=password)
self.assertEqual(User.objects.filter(email=user.email).count(), 1)
# Rolls back the transaction to savepoint sid.
transaction.savepoint_rollback(sid)
【讨论】: