# Chapter 6. Improving Functional Tests: Ensuring Isolation and Removing Voodoo Sleeps ## 6.1 Ensuring Test Isolation in Functional Tests * Problem left on scratchpad: Clean up after FT runs * Methods: * 1. In `functional_tests.py` add in codes to delete database * 2. Use class `LiveServerTestCase` (Django 1.4). It will create a test db and start up a dev server for functional tests How to use `LiveServerTestCase`: * It's derived from Django's `TestCase` class, so can be run by Django test runner * Django test runner search for all scripts start with `test` What to do?: 1. Create a `functional_test` directory, i.e. create a *functional_test* app 2. rename `functional_test.py` as `tests.py` and move it into `functional_test` directory 3. Modify `tests.py` to let it use `LiveServerTestCase` as shown below, and instead of hardcoding visit to localhost port 8000, `LiveserverTestCase` provide attribute `live_server_url`. ```python class NewVisitorTest(LiveServerTestCase): ... def test_can_start_a_list_and_retrieve_it_later(self): # Edith has heard about a cool new online to-do app. She goes # to check out its homepage self.browser.get(self.live_server_url) ``` ### 6.1.1 Running Just the Unit Tests We can specify what we want to run by ``` python manage.py test functional_tests ``` or ``` python manage.py test lists ``` ## 6.2 Aside: Upgrading Selenium and Geckodriver Sometimes, mismatch between firefox and (Selenium + Geckodriver) may result failure in test, as firefox may auto upgrade itself at night. Upgrade Selenium + Geckodriver as follow: 1. `pip install --upgrade selenium` 2. Download new geckodriver ## 6.3 On Implicit and Explicit Waits, and Voodoo time.sleeps Implicit Waits & Explicit Waits: * Implicit Waits: Selenium tries to wait "automatically" for you when it thinks the page is loading. But it's unstable, DON'T USE IT. * Explicit Waits: `time.sleep(...)` in FT. It's hard to set a fixed second (Voodoo Sleep). A while loop with try/except will be more better ```python from selenium.common.exceptions import WebDriverException MAX_WAIT = 10 # Maximum amount of time we're prepared to wait [...] def wait_for_row_in_list_table(self, row_text): start_time = time.time() while True: try: table = self.browser.find_element_by_id('id_list_table') rows = table.find_elements_by_tag_name('tr') self.assertIn(row_text, [row.text for row in rows]) return except (AssertionError, WebDriverException) as e: if time.time() - start_time > MAX_WAIT: raise e time.sleep(0.5) ``` Replace all `check_for_row_in_list_table` with `wait_for_row_in_list_table` ### "BEST PRACTICES" Applied in this Chapter * Ensuring test isolation and managing global state * Avoid "voodoo" sleeps (i.e. `time.sleep`) * Don't rely on Selenium's implicit waits