使用py.test,LiveServerTestCase后不会重置数据库

时间:2021-05-22 19:20:20

I have a number of Django tests and typically run them using py.test. I recently added a new test case in a new file test_selenium.py. This Test Case has uses the LiveServerTestCase and StaticLiveServerTestCase classes (which is a first for me, usually I am using just TestCase).

我有许多Django测试,通常使用py.test运行它们。我最近在一个新文件test_selenium.py中添加了一个新的测试用例。这个测试用例使用了LiveServerTestCase和StaticLiveServerTestCase类(对我来说这是第一个,通常我只使用TestCase)。

Adding this new batch of tests in this new file has caused subsequent tests to start failing in py.test (when before they all passed). It appears that the database is not being "reset" after the LiveServerTestCase in py.test. I can tell because of the incrementation of my model's pk values.

在这个新文件中添加这一批新测试导致后续测试在py.test中失败(在它们全部通过之前)。在py.test中的LiveServerTestCase之后,似乎没有“重置”数据库。我可以告诉我,因为我的模型的pk值增加了。

When I run these tests using the Django test runner, they all pass and the pk's are reset in subsequent tests; in the py.test test runner the pk's are being incremented in subsequent tests after the LiveServerTestCase is run. So if I have hardcoded in my test to create an object and retrieve it based on the pk I am expecting it fails because the databases are different between Django and py.test.

当我使用Django测试运行器运行这些测试时,它们都会通过,并且pk会在后续测试中重置;在py.test测试运行器中,在运行LiveServerTestCase之后,pk将在后续测试中递增。因此,如果我在我的测试中硬编码来创建一个对象并基于pk检索它我期望它失败,因为Django和py.test之间的数据库是不同的。

Any ideas why this might be and how to fix it?

任何想法为什么会这样,以及如何解决它?

New test test causing the DB behavior:

from django.contrib.staticfiles.testing import StaticLiveServerTestCase

from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By


class UpdateCountSelenium(StaticLiveServerTestCase):

    def setUp(self):
        self.selenium = webdriver.Firefox()
        self.delay = 3

    def tearDown(self):
        self.selenium.quit()

    def test_ajax_hit(self):
        self.selenium.get("%s%s" % (self.live_server_url, '/1/'))
        # waits for javascript to load and tests the ajax view
        wait = WebDriverWait(self.selenium, 3)
        response = wait.until(EC.text_to_be_present_in_element((By.ID, 'counted-value'), 'true'))
        self.assertTrue(response)

1 个解决方案

#1


4  

A LiveServerTestCase and it's subclass StaticLiveServerTestCase both inherit from TransactionTestCase which differs from TestCase in the manner it resets the DB on test case tearDown. Here is the quote from the aforementioned documentation:

LiveServerTestCase及其子类StaticLiveServerTestCase都继承自TransactionTestCase,它与TestCase的不同之处在于它在测试用例tearDown上重置数据库的方式。以下是上述文件的引用:

Django’s TestCase class (described below) makes use of database transaction facilities to speed up the process of resetting the database to a known state at the beginning of each test. A consequence of this, however, is that some database behaviors cannot be tested within a Django TestCase class. For instance, you cannot test that a block of code is executing within a transaction, as is required when using select_for_update(). In those cases, you should use TransactionTestCase.

Django的TestCase类(如下所述)利用数据库事务工具来加速在每次测试开始时将数据库重置为已知状态的过程。然而,其结果是无法在Django TestCase类中测试某些数据库行为。例如,您无法测试事务中是否正在执行代码块,这在使用select_for_update()时是必需的。在这些情况下,您应该使用TransactionTestCase。

TransactionTestCase and TestCase are identical except for the manner in which the database is reset to a known state and the ability for test code to test the effects of commit and rollback:

TransactionTestCase和TestCase是相同的,除了数据库重置为已知状态的方式以及测试代码测试提交和回滚效果的能力:

  • A TransactionTestCase resets the database after the test runs by truncating all tables. A TransactionTestCase may call commit and rollback and observe the effects of these calls on the database.

    TransactionTestCase在测试运行后通过截断所有表来重置数据库。 TransactionTestCase可以调用commit和rollback,并观察这些调用对数据库的影响。

  • A TestCase, on the other hand, does not truncate tables after a test. Instead, it encloses the test code in a database transaction that is rolled back at the end of the test. This guarantees that the rollback at the end of the test restores the database to its initial state.

    另一方面,TestCase在测试后不会截断表。相反,它将测试代码包含在数据库事务中,该事务在测试结束时回滚。这可以保证测试结束时的回滚将数据库恢复到其初始状态。

As you mentioned, you see the PK counter retained. This is because truncating tables, means dropping all rows, but this generally does not mean the PK counter is reset.

正如您所提到的,您会看到PK计数器保留。这是因为截断表意味着丢弃所有行,但这通常并不意味着PK计数器被重置。

I assume you care about this because you are using asserting objects by specifying a PK (e.g assert YourModel.objects.filter(pk=1).exists().

我假设您关心这个,因为您通过指定PK来使用断言对象(例如断言YourModel.objects.filter(pk = 1).exists()。

Instead, I suggest that in your tests, you assert the existence of X objects (e.g assert YourModel.objects.count() == 1, or even assert the specific objects you expect to exist) and then use these objects in your test as you would usually.

相反,我建议你在测试中断言X对象的存在(例如断言YourModel.objects.count()== 1,甚至断言你希望存在的特定对象),然后在你的测试中使用这些对象作为你通常会。

#1


4  

A LiveServerTestCase and it's subclass StaticLiveServerTestCase both inherit from TransactionTestCase which differs from TestCase in the manner it resets the DB on test case tearDown. Here is the quote from the aforementioned documentation:

LiveServerTestCase及其子类StaticLiveServerTestCase都继承自TransactionTestCase,它与TestCase的不同之处在于它在测试用例tearDown上重置数据库的方式。以下是上述文件的引用:

Django’s TestCase class (described below) makes use of database transaction facilities to speed up the process of resetting the database to a known state at the beginning of each test. A consequence of this, however, is that some database behaviors cannot be tested within a Django TestCase class. For instance, you cannot test that a block of code is executing within a transaction, as is required when using select_for_update(). In those cases, you should use TransactionTestCase.

Django的TestCase类(如下所述)利用数据库事务工具来加速在每次测试开始时将数据库重置为已知状态的过程。然而,其结果是无法在Django TestCase类中测试某些数据库行为。例如,您无法测试事务中是否正在执行代码块,这在使用select_for_update()时是必需的。在这些情况下,您应该使用TransactionTestCase。

TransactionTestCase and TestCase are identical except for the manner in which the database is reset to a known state and the ability for test code to test the effects of commit and rollback:

TransactionTestCase和TestCase是相同的,除了数据库重置为已知状态的方式以及测试代码测试提交和回滚效果的能力:

  • A TransactionTestCase resets the database after the test runs by truncating all tables. A TransactionTestCase may call commit and rollback and observe the effects of these calls on the database.

    TransactionTestCase在测试运行后通过截断所有表来重置数据库。 TransactionTestCase可以调用commit和rollback,并观察这些调用对数据库的影响。

  • A TestCase, on the other hand, does not truncate tables after a test. Instead, it encloses the test code in a database transaction that is rolled back at the end of the test. This guarantees that the rollback at the end of the test restores the database to its initial state.

    另一方面,TestCase在测试后不会截断表。相反,它将测试代码包含在数据库事务中,该事务在测试结束时回滚。这可以保证测试结束时的回滚将数据库恢复到其初始状态。

As you mentioned, you see the PK counter retained. This is because truncating tables, means dropping all rows, but this generally does not mean the PK counter is reset.

正如您所提到的,您会看到PK计数器保留。这是因为截断表意味着丢弃所有行,但这通常并不意味着PK计数器被重置。

I assume you care about this because you are using asserting objects by specifying a PK (e.g assert YourModel.objects.filter(pk=1).exists().

我假设您关心这个,因为您通过指定PK来使用断言对象(例如断言YourModel.objects.filter(pk = 1).exists()。

Instead, I suggest that in your tests, you assert the existence of X objects (e.g assert YourModel.objects.count() == 1, or even assert the specific objects you expect to exist) and then use these objects in your test as you would usually.

相反,我建议你在测试中断言X对象的存在(例如断言YourModel.objects.count()== 1,甚至断言你希望存在的特定对象),然后在你的测试中使用这些对象作为你通常会。