我正在使用pytest
为一些使用peewee
实现的数据库模型编写一组单元测试。我想使用数据库事务(数据库是Postgres,如果这是相关的),以便在每次测试后回滚任何数据库更改。
我有一种情况,我想在测试中使用两个灯具,但让两个灯具通过rollback
方法清理他们的数据库模型,如下所示:
@pytest.fixture
def test_model_a():
with db.transaction() as txn: # `db` is my database object
yield ModelA.create(...)
txn.rollback()
@pytest.fixture
def test_model_b():
with db.transaction() as txn: # `db` is my database object
yield ModelB.create(...)
txn.rollback()
def test_models(test_model_a, test_model_b):
# ...
这有效,但阅读documentation for peewee
表明这很容易出错:
如果您尝试使用
transaction()
上下文管理器与peewee嵌套事务,则只会使用最外层事务。但是,如果嵌套块中发生异常,则可能导致不可预测的行为,因此强烈建议您使用atomic()
。
但是,atomic()
不提供rollback()
方法。似乎在明确管理事务时,关键是使用最外层transaction()
,并在该事务中使用savepoint()
上下文管理器。但是在我上面的测试代码中,两个灯具都处于相同的“级别”,可以这么说,我不知道在哪里创建事务,以及在哪里创建保存点。
我唯一的另一个想法是使用夹具得到评估的顺序来决定放置交易的位置(which seems to be alphabetical),但这看起来确实非常脆弱。
有没有办法实现这个目标?或者我的测试设计是否需要重新思考?
答案 0 :(得分:4)
如果要回滚在测试中创建的所有事务,您可以使用一个夹具来处理事务本身并使模型夹具使用它:
@pytest.fixture
def transaction():
with db.transaction() as txn: # `db` is my database object
yield txn
txn.rollback()
@pytest.fixture
def test_model_a(txn):
yield ModelA.create(...)
@pytest.fixture
def test_model_b(txn):
yield ModelB.create(...)
def test_models(test_model_a, test_model_b):
# ...
这样,所有模型都在同一事务中创建,并在测试结束时回滚。
答案 1 :(得分:0)
如果您要对干净的数据库运行单独的测试,则可以使用peewee文档中指定的Database.bind_ctx()
和Model.bind_ctx()
方法:
http://docs.peewee-orm.com/en/latest/peewee/database.html#testing-peewee-applications