4.9 KiB
Chapter 4. What Are We Doing with All These Tests? (And, Refactoring)
4.1 Programming is like pulling a bucket of water up from a well
TDD is discipline. Its payoffs not come immediately
4.2 Using selenium to test user interactions
Modify functional_tests.py
to test user interactions
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import time
import unittest
class NewVisitorTest(unittest.TestCase):
def setUp(self):
self.browser = webdriver.Firefox()
def tearDown(self):
self.browser.quit()
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('http://localhost:8000')
# She notices the page title and header mention to-do lists
self.assertIn('To-Do', self.browser.title)
header_text = self.browser.find_element_by_tag_name('h1').text
self.assertIn('To-Do', header_text)
# She is invited to enter a to-do item straight away
inputbox = self.browser.find_element_by_id('id_new_item')
self.assertEqual(
inputbox.get_attribute('placeholder'),
'Enter a to-do item'
)
# She types "Buy peacock feathers" into a text box (Edith's hobby
# is tying fly-fishing lures)
inputbox.send_keys('Buy peacock feathers')
# When she hits enter, the page updates, and now the page lists
# "1: Buy peacock feathers" as an item in a to-do list
inputbox.send_keys(Keys.ENTER)
time.sleep(1)
table = self.browser.find_element_by_id('id_list_table')
rows = table.find_element_by_tag_name('tr')
self.assertTrue(
any(row.text == '1: Buy peacock feathers' for row in rows)
)
# There is still a text box inviting her to add another item. She
# enters "Use peacock feathers to make a fly" (Edith is very methodical)
self.fail('Finish the test!')
# The page updates again, and now shows both items on her list
# Edith wonders whether the site will remember her list. Then she sees
# that the site has generated a unique URL for her -- there is some
# explanatory text to that effect.
# She visits that URL - her to-do list is still there.
# Satisfied, she goes back to sleep
if __name__ == '__main__':
unittest.main(warnings='ignore')
Start django server and then run python functional_tests.py
4.3 The "Don't Test Constants" Rule, and Templates to the Rescue
Our object: test HTML.
Problem: currently test case look for exact HTML string, directly test content within HTML using fixed string is not wise.
Testing content within a HTML is not useful, as it's testing constant.
Unit tests are about testing logic, flow control, and configuration.
Using template is much better solution to test raw strings in Python.
4.3.1 Refactoring to Use a Template
Our object: refactor view function return the same HTML
Refactor = improve the code without changing its functionality
- Create a html file in
lists/template
folder, in which Django look for template that used for the app - django use
render
function to render the template, given request - Add app into
superlists/settings.py
'sINSTALLED_APPS
list, so django will search it
Check commit 89224c9
4.3.2 The Django Test Client
Django provides Django Test Client which can be used to check what templates are used.
request = HttpRequest() #1
response = home_page(request) #2
can be replaced by (django test client)
response = self.client.get('/') #1
And assert methods
self.assertTrue(html.startswith('<html>'))
self.assertIn('<title>To-Do lists</title>', html)
self.assertTrue(html.strip().endswith('</html>'))
can be replaced by
self.assertTemplateUsed(response, 'home.html') #3
Note: DJANGO TEST CLIENT is introduced until now because manual test steps are necessary for learning
Check commit 747e0c7
4.4 On Refactoring
Tip: When refactoring, work on either the code or the tests, but not both at once, otherwise, changing both will quickly lead to change across multiple files and chaos
4.5 A Little More of Our Front Page
Recap: The TDD Process
Main aspects of TDD process:
- Functional tests
- Unit tests
- The unit-test/code cycle
- Refactoring
Overall TDD process is:
Ques: How does FT, UT cooperate with TDD?
ANS: FT is a larger TDD cycle, while UTs are smaller TDD cycles
- Write a FT and see it fail.
- mini-TDD cycle of each unit test is employed to achieve small steps
QUES: How does refactoring cooperate with TDD?
ANS: Use a FT to check the behavior of app, FT is fixed but UTs can be add/removed
Overall, it's a "Double-Loop TDD" as shown below