With py.test, database is not reset after LiveServ

2019-04-25 06:17发布

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

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.

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.

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条回答
唯我独甜
2楼-- · 2019-04-25 06:30

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:

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.

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:

  • 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.

  • 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.

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.

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

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.

查看更多
登录 后发表回答