From a343a84ce6a8e7e0de2998ac423cbc1cecdb9aea Mon Sep 17 00:00:00 2001 From: Julien Phalip Date: Wed, 28 Dec 2011 22:20:30 +0000 Subject: [PATCH] =?UTF-8?q?Added=20the=20`wait=5Funtil()`=20and=20`wait=5F?= =?UTF-8?q?loaded=5Ftag()`=20methods=20to=20`AdminSeleniumWebDriverTestCas?= =?UTF-8?q?e`=20to=20prevent=20some=20concurrency=20issues=20with=20in-mem?= =?UTF-8?q?ory=20SQLite=20database=20access=20in=20the=20admin=20Selenium?= =?UTF-8?q?=20tests.=20Thanks=20to=20Florian=20Apolloner,=20Anssi=20Ka?= =?UTF-8?q?=CC=88a=CC=88ria=CC=88inen=20and=20Aymeric=20Augustin=20for=20t?= =?UTF-8?q?heir=20help=20debugging=20this=20problem.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git-svn-id: http://code.djangoproject.com/svn/django/trunk@17283 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/contrib/admin/tests.py | 22 ++++++++++++ docs/topics/testing.txt | 35 ++++++++++++++++++++ tests/regressiontests/admin_inlines/tests.py | 3 ++ 3 files changed, 60 insertions(+) diff --git a/django/contrib/admin/tests.py b/django/contrib/admin/tests.py index 2a3f0c3cd1..1cb5e8b6a4 100644 --- a/django/contrib/admin/tests.py +++ b/django/contrib/admin/tests.py @@ -29,6 +29,26 @@ class AdminSeleniumWebDriverTestCase(LiveServerTestCase): if hasattr(cls, 'selenium'): cls.selenium.quit() + def wait_until(self, callback, timeout=10): + """ + Helper function that blocks the execution of the tests until the + specified callback returns a value that is not falsy. This function can + be called, for example, after clicking a link or submitting a form. + See the other public methods that call this function for more details. + """ + from selenium.webdriver.support.wait import WebDriverWait + WebDriverWait(self.selenium, timeout).until(callback) + + def wait_loaded_tag(self, tag_name, timeout=10): + """ + Helper function that blocks until the element with the given tag name + is found on the page. + """ + self.wait_until( + lambda driver: driver.find_element_by_tag_name(tag_name), + timeout + ) + def admin_login(self, username, password, login_url='/admin/'): """ Helper function to log into the admin. @@ -41,6 +61,8 @@ class AdminSeleniumWebDriverTestCase(LiveServerTestCase): login_text = _('Log in') self.selenium.find_element_by_xpath( '//input[@value="%s"]' % login_text).click() + # Wait for the next page to be loaded. + self.wait_loaded_tag('body') def get_css_value(self, selector, attribute): """ diff --git a/docs/topics/testing.txt b/docs/topics/testing.txt index 727ef2cb8e..acb24c0a98 100644 --- a/docs/topics/testing.txt +++ b/docs/topics/testing.txt @@ -1843,6 +1843,41 @@ out the `full reference`_ for more details. ` so you'll need to have your project configured accordingly (in particular by setting :setting:`STATIC_URL`). +.. note:: + + When using an in-memory SQLite database to run the tests, the same database + connection will be shared by two threads in parallel: the thread in which + the live server is run, and the thread in which the test case is run. It is + important to prevent simultaneous database queries via this shared + connection by the two threads as that may sometimes cause the tests to + randomly fail. So you need to ensure that the two threads do not access the + database at the same time. In particular, this means that in some cases + (for example just after clicking a link or submitting a form) you might + need to check that a response is received by Selenium and that the next + page is loaded before proceeding further with the execution of the tests. + This can be achieved, for example, by making Selenium wait until the + `` HTML tag is found in the response: + + .. code-block:: python + + def test_login(self): + from selenium.webdriver.support.wait import WebDriverWait + ... + self.selenium.find_element_by_xpath('//input[@value="Log in"]').click() + # Wait until the response is received + WebDriverWait(self.selenium, timeout).until( + lambda driver: driver.find_element_by_tag_name('body'), timeout=10) + + The difficult point is that there really is no such thing as a "page load", + especially in modern Web apps that have dynamically-generated page + components that do not exist in the HTML initially received from the + server. So simply checking for the presence of the `` tag in the + response might not necessarily be appropriate for all use cases. Please + refer to the `Selenium FAQ`_ and the `Selenium documentation`_ for more + information on this topic. + + .. _Selenium FAQ: http://code.google.com/p/selenium/wiki/FrequentlyAskedQuestions#Q:_WebDriver_fails_to_find_elements_/_Does_not_block_on_page_loa + .. _Selenium documentation: http://seleniumhq.org/docs/04_webdriver_advanced.html#explicit-waits Using different testing frameworks ================================== diff --git a/tests/regressiontests/admin_inlines/tests.py b/tests/regressiontests/admin_inlines/tests.py index 7b417d8995..3ea2ce476e 100644 --- a/tests/regressiontests/admin_inlines/tests.py +++ b/tests/regressiontests/admin_inlines/tests.py @@ -443,6 +443,9 @@ class SeleniumFirefoxTests(AdminSeleniumWebDriverTestCase): self.selenium.find_element_by_name('profile_set-2-last_name').send_keys('2 last name 2') self.selenium.find_element_by_xpath('//input[@value="Save"]').click() + # Wait for the next page to be loaded. + self.wait_loaded_tag('body') + # Check that the objects have been created in the database self.assertEqual(ProfileCollection.objects.all().count(), 1) self.assertEqual(Profile.objects.all().count(), 3)