ishikun
ishikun

Reputation: 428

Selenium Tests very slow in between individual tests

I'm using Django 1.4, Selenium 2.53.1 and Chrome Webdriver 2.21 as my testing webdriver to test my Django app.

I initialize my class as such:

class SeleniumTest(LiveServerTestCase):

@classmethod
def setUpClass(cls):
    cls.display = Display(visible=0, size=(800, 600))
    cls.display.start()
    cls.driver = webdriver.Chrome()
    cls.driver.set_page_load_timeout(15)
    cls.driver.maximize_window()
    super(SeleniumTest, cls).setUpClass()

def setUp(self):

    settings.SESSION_ENGINE = 'django.contrib.sessions.backends.db'
    engine = import_module(settings.SESSION_ENGINE)
    self.sessionStore = engine.SessionStore()
    self.sessionStore.save()
    username = 'hello'
    password = 'hello'
    self.cad_user, created = User.objects.get_or_create(username=username, email='[email protected]')
    self.cad_user.set_password(password)
    self.cad_user.save()

    try:
        self.get_url('login')
        if self.driver.title == 'Login':
            self.driver.find_element_by_id('id_username').send_keys(username)
            self.driver.find_element_by_id('id_password').send_keys(password)
            self.driver.find_element_by_css_selector('input[type="submit"]').click()

An example one of my tests is below. It tests a dropdown with multiple levels that appear after you move your mouse over them and checks that they go to correct link

def dropdown_check(self, header_ids, choice_id, title):
    choice = self.driver.find_element_by_id(choice_id)
    mouse = webdriver.ActionChains(self.driver)
    for header_id in header_ids:
        header_element = self.driver.find_element_by_id(header_id)
        WebDriverWait(self.driver, 1).until(EC.element_to_be_clickable((By.ID, header_id)))
        mouse.move_to_element(header_element)
        mouse.perform()
    WebDriverWait(self.driver, 1).until(EC.element_to_be_clickable((By.ID, choice_id)))
    choice.click()
    self.assertEquals(self.driver.title, title)

def test_my_status_navigation(self):
    self.dropdown_check(['menubar_my_status'], 'menubar_my_status', 'User Status')

I've tried these things:

  1. I've timed the code for each test, they take less than a second.
  2. I've also timed the setup and setupclass methods, and they take maximum 2 seconds.
  3. I've set to set_page_load_timeout to 0 and it does not change overal execution time.
  4. I've run the tests by adding one extra test each round and discovered that for each test there is an increase of around 40 seconds to the total test suite time.

Given this, the entire suite of 8 tests takes over 300+ seconds and I have no idea why. I'm sure the loading of Webdriver takes some time, but after each individual test ends, I can see the Webdriver just sit there and do nothing.

Upvotes: 2

Views: 1031

Answers (2)

e4c5
e4c5

Reputation: 53754

The biggest issue is that LiveServerTestCase is the slowest of all the test cases in the Django Project. It inherits from TransactionTestCase

TransactionTestCase inherits from SimpleTestCase to add some database-specific features:

Resetting the database to a known state at the beginning of each test to ease testing and using the ORM.

and

Django’s TestCase class is a more commonly used subclass of TransactionTestCase that 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.

Thus each of your tests results in the database being completely reset, which is really slow. One solution is to use the -k or --keep option.

./manage.py test -k myapp

This will cut off at least 100 seconds from your test execution time.

There is another solution that cannot be applied in all conditions because TestCase cannot be used for Selenium Tests. However you can write independent selenium tests that connect to the dev server to speed some tests up. In this case you use unittest.TestCase from from python instead of django.test.testcases.TestCase. That sounds totally confusing so let me give an example!

from unittest import TestCase
from selenium import webdriver

class SeleniumTest(TestCase):


    def setUp(self):
        # usual code to setup drivers etc

    def testSomething(self):
        self.driver.get('localhost:8000/somepage')

        # now you are connecting directly to the development server.  
        # this approach is not suitable for all requirements but 
        # very fast compared to using a LiveServerTestCase

Last but not least: Often you don't need LiveServerTestCase or selenium tests at all. It's much faster and easier to use the django test client

Upvotes: 4

ishikun
ishikun

Reputation: 428

I've narrowed it down to something to do with Django and how it interacts with the database because I've run with setup and setUpClass as well as my test methods as:

def setUpClass(cls):
    pass

And the overall time does not change and changing the database to sqlite3 significantly decreases the total test time albiet some errors.

Upvotes: 0

Related Questions