Added the `wait_until()` and `wait_loaded_tag()` methods to `AdminSeleniumWebDriverTestCase` 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: http://code.djangoproject.com/svn/django/trunk@17283 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
46c12d1293
commit
a343a84ce6
|
@ -29,6 +29,26 @@ class AdminSeleniumWebDriverTestCase(LiveServerTestCase):
|
||||||
if hasattr(cls, 'selenium'):
|
if hasattr(cls, 'selenium'):
|
||||||
cls.selenium.quit()
|
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/'):
|
def admin_login(self, username, password, login_url='/admin/'):
|
||||||
"""
|
"""
|
||||||
Helper function to log into the admin.
|
Helper function to log into the admin.
|
||||||
|
@ -41,6 +61,8 @@ class AdminSeleniumWebDriverTestCase(LiveServerTestCase):
|
||||||
login_text = _('Log in')
|
login_text = _('Log in')
|
||||||
self.selenium.find_element_by_xpath(
|
self.selenium.find_element_by_xpath(
|
||||||
'//input[@value="%s"]' % login_text).click()
|
'//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):
|
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
|
</howto/static-files>` so you'll need to have your project configured
|
||||||
accordingly (in particular by setting :setting:`STATIC_URL`).
|
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 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 `<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: 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
|
Using different testing frameworks
|
||||||
==================================
|
==================================
|
||||||
|
|
|
@ -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_name('profile_set-2-last_name').send_keys('2 last name 2')
|
||||||
self.selenium.find_element_by_xpath('//input[@value="Save"]').click()
|
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
|
# Check that the objects have been created in the database
|
||||||
self.assertEqual(ProfileCollection.objects.all().count(), 1)
|
self.assertEqual(ProfileCollection.objects.all().count(), 1)
|
||||||
self.assertEqual(Profile.objects.all().count(), 3)
|
self.assertEqual(Profile.objects.all().count(), 3)
|
||||||
|
|
Loading…
Reference in New Issue