This is a learning note to help you understand Test Driven Development (TDD) in Django.
References are [** Test-Driven Development with Python: Obey the Testing Goat: Using Django, Selenium, and JavaScript (English Edition) 2nd Edition **](https://www.amazon.co.jp/dp/B074HXXXLS We will proceed with learning based on / ref = dp-kindle-redirect? _ Encoding = UTF8 & btkr = 1).
In this book, we are conducting functional tests using Django 1.1 series and FireFox, but this time we will carry out functional tests on Djagno 3 series and Google Chrome. I've also made some personal modifications (such as changing the Project name to Config), but there are no major changes.
⇒⇒ Click here for Part 1
Part1. The Basics of TDD and Django
Chapter2 Extending Our Functional Test Usinng the unittest Module
In Chapter 1, you can write the first functional test from Django's environment construction and check whether Django's Default page is working through the functional test. It's done. This time I would like to apply this to the actual front page while creating a ToDo application.
Testing with Chrome's web driver and Selenium is called ** functional testing ** because it allows you to see ** how your application works ** from the user's perspective. It traces the * user story * when a user uses an application, and determines how the user uses the application and how the application responds to it.
Functional Test == Acceptance Test == End-To-End Test
** Test-Driven Development with Python: Obey the Testing Goat: Using Django, Selenium, and JavaScript (English Edition) 2nd Edition ** In / B074HXXXLS / ref = dp-kindle-redirect? _ Encoding = UTF8 & btkr = 1), testing the function (* function *) of an application is called * functional tests *, so in this article it is called a functional test. I will. This is also called * acceptance tests *, * End-To-End tests (E2E tests, integration tests) *. The purpose of this test is to see how the entire application works from the outside.
Let's write it as a comment in the functional test while assuming the actual user story.
# django-tdd/functional_tests.py
from selenium import webdriver
browser = webdriver.Chrome()
#Nobita is a new to-I heard that there is a do app and accessed the homepage.
browser.get('http://localhost:8000')
#Nobita has the page title and header to-I confirmed that it suggests that it is a do app.
assert 'To-Do' in browser.title
#Nobita is to-Prompted to fill in the do item,
#Nobita wrote in the text box "Buy Dorayaki"(His best friend loves dorayaki)
#When Nobita presses enter, the page is refreshed
# "1:Buying dorayaki"Is to-Found to be added as an item to the do list
#The text box allows you to continue to fill in items, so
#Filled in "Billing Dorayaki Money"(He is tight when it comes to money)
#The page was refreshed again and I was able to see that new items were added
#Nobita is this to-I was wondering if the do app was recording my items properly,
#When I checked the URL, I found that the URL seems to be a specific URL for Nobita
#When Nobita tried to access a specific URL that he had confirmed once,
#The item was saved so I was happy to fall asleep.
browser.quit()
Changed the title assertion from "Django" to "To-Do". It is expected that the functional test will fail as it is. So let's run a functional test.
#Start local server
$ python manage.py runserver
#Launch another command line
#Run functional test
$ python functional_tests.py
Traceback (most recent call last):
File "functional_tests.py", line 11, in <module>
assert 'To-Do' in browser.title
AssertionError
The test failed as expected. Therefore, we know that we should proceed with development so that this test can be successful.
In the functional test we just ran
--AssertioError is unfriendly (I hope you know what the browser title really was)
--The browser started by Selenium is not erased and remains
There was annoyance. These can be solved using the unittest module, which is a standard Python module.
Let's rewrite the functional test as follows.
# django-tdd/functional_tests.py
from selenium import webdriver
import unittest
class NewVisitorTest(unittest.TestCase):
def setUp(self):
self.browser = webdriver.Chrome()
def tearDown(self):
self.browser.quit()
def test_can_start_a_list_and_retrieve_it_later(self):
#Nobita is a new to-I heard that there is a do app and accessed the homepage.
self.browser.get('http://localhost:8000')
#Nobita has the page title and header to-I confirmed that it suggests that it is a do app.
self.assertIn('To-Do', self.browser.title)
self.fail('Finish the test!')
#Nobita is to-Prompted to fill in the do item,
#Nobita wrote in the text box "Buy Dorayaki"(His best friend loves dorayaki)
#When Nobita presses enter, the page is refreshed
# "1:Buying dorayaki"Is to-Found to be added as an item to the do list
#The text box allows you to continue to fill in items, so
#Filled in "Billing Dorayaki Money"(He is tight on money)
#The page was refreshed again and I was able to see that new items were added
#Nobita is this to-I was wondering if the do app was recording my items properly,
#When I checked the URL, I found that the URL seems to be a specific URL for Nobita
#When Nobita tried to access a specific URL that he had confirmed once,
#The item was saved so I was happy to fall asleep.
if __name__ == '__main__':
unittest.main(warnings='ignore')
Functional tests can be written by inheriting unittest.TestCase. Let's sort out the points this time.
--The test function you want to execute starts with * test_ , and the test runner automatically runs the test. - setUp * and * tearDown * are special functions that are executed before and after the test runs. -* tearDown * is also executed with a test error. --Self.fail () will fail the test and spit out an error. --Unittest.main () runs the test and automatically runs the test case and its methods. --Warnings = You can ignore excessive ResoureWarning etc. by adding the option of'ignore'.
Let's run it.
$ python functional_tests.py
======================================================================
FAIL: test_can_start_a_list_and_retrieve_it_later (__main__.NewVisitorTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "functional_tests.py", line 20, in test_can_start_a_list_and_retrieve_it_later
self.assertIn('To-Do', self.browser.title)
AssertionError: 'To-Do' not found in 'Django:A web framework for perfectionists who never miss a deadline'
----------------------------------------------------------------------
Ran 1 test in 7.956s
FAILED (failures=1)
The unittest module makes it easier to understand the contents of * AssertionError *.
If you change * self.assertIn ('To-Do', self.brower.title) * to * self.assertIn ('Django', self.brower.title) *, you should be able to clear the test. Let's check this.
# django-tdd/functional_tests.py
# ~~abridgement~~
def test_can_start_a_list_and_retrieve_it_later(self):
#Nobita is a new to-I heard that there is a do app and accessed the homepage.
self.browser.get('http://localhost:8000')
#Nobita has the page title and header to-I confirmed that it suggests that it is a do app.
self.assertIn('Django', self.browser.title) #Change
self.fail('Finish the test!')
# ~~abridgement~~
$ python functinal_tests.py
======================================================================
FAIL: test_can_start_a_list_and_retrieve_it_later (__main__.NewVisitorTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "functional_tests.py", line 21, in test_can_start_a_list_and_retrieve_it_later
self.fail('Finish the test!')
AssertionError: Finish the test!
----------------------------------------------------------------------
Ran 1 test in 6.081s
FAILED (failures=1)
As a result of running, the test should succeed, but the test is FAIL. When I checked * AssertionError *, the message set by * self.fail ('Finish the test') * was reflected. This is because * self.fail ('message') * has a function to always spit out the set error message even if there is no error. Here it is set as a reminder to let you know that the test is over.
So this time, comment out * self.fail ('Finish the test!') * And run it.
# django-tdd/functional_tests.py
# ~~abridgement~~
def test_can_start_a_list_and_retrieve_it_later(self):
#Nobita is a new to-I heard that there is a do app and accessed the homepage.
self.browser.get('http://localhost:8000')
#Nobita has the page title and header to-I confirmed that it suggests that it is a do app.
self.assertIn('Django', self.browser.title)
# self.fail('Finish the test!') #Comment out
# ~~abridgement~~
$ python functinal_tests.py
.
----------------------------------------------------------------------
Ran 1 test in 7.698s
OK
It was confirmed that there were no errors and the test was successful. The final functional test output should look like this. Let's undo the changes in * functional_tests.py *.
# django-tdd/functional_tests.py
# ~~abridgement~~
def test_can_start_a_list_and_retrieve_it_later(self):
#Nobita is a new to-I heard that there is a do app and accessed the homepage.
self.browser.get('http://localhost:8000')
#Nobita has the page title and header to-I confirmed that it suggests that it is a do app.
self.assertIn('To-do', self.browser.title)
self.fail('Finish the test!')
# ~~abridgement~~
Commit
By creating a user story while adding comments, I was able to create and run a functional test of my to-do application. Let's commit here. I was able to confirm the changed file by doing ** git status **.
You can check the difference from the last commit by doing ** git diff **. When you do this, you can see that functional_tests.py has changed significantly.
Let's commit.
$ git add .
$ git commit -m "First FT specced out in comments, and now users unittest"
I was able to change the functional tests from a level that confirms the launch of a Django project to one that is based on a user story that uses a To-Do application. We also found that using unittest can make better use of error messages.
Recommended Posts