Browse files

Added the `wait_until()` and `wait_loaded_tag()` methods to `AdminSel…

…eniumWebDriverTestCase` to prevent some concurrency issues with in-memory SQLite database access in the admin Selenium tests. Thanks to Florian Apolloner, Anssi Kääriäinen and Aymeric Augustin for their help debugging this problem.

git-svn-id: bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
1 parent 46c12d1 commit a343a84ce6a8e7e0de2998ac423cbc1cecdb9aea @jphalip jphalip committed Dec 28, 2011
Showing with 60 additions and 0 deletions.
  1. +22 −0 django/contrib/admin/
  2. +35 −0 docs/topics/testing.txt
  3. +3 −0 tests/regressiontests/admin_inlines/
@@ -29,6 +29,26 @@ def tearDownClass(cls):
if hasattr(cls, 'selenium'):
+ 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 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 @@ def admin_login(self, username, password, login_url='/admin/'):
login_text = _('Log in')
'//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):
@@ -1843,6 +1843,41 @@ out the `full reference`_ for more details.
</howto/static-files>` 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
+ `<body>` HTML tag is found in the response:
+ .. code-block:: python
+ def test_login(self):
+ from 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 `<body>` 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:
+ .. _Selenium documentation:
Using different testing frameworks
@@ -443,6 +443,9 @@ def test_add_inlines(self):
self.selenium.find_element_by_name('profile_set-2-last_name').send_keys('2 last name 2')
+ # 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)

0 comments on commit a343a84

Please sign in to comment.