# Chapter 11. Testing Your Code Learn how to test code using Python's `unittest` module. ## Testing a Function Function to be tested as shown below. ```python def get_formatted_name(first, last): """Generate a neatly formatted full name.""" full_name = f"{first} {last}" return full_name.title() ``` A script that should use `get_formatted_name` ```python from name_function import get_formatted_name print("Enter 'q' at any time to quit.") while True: first = input("\nPlease give me a first name: ") if first == 'q': break last = input("Please give me a last name: ") if last == 'q': break formatted_name = get_formatted_name(first, last) print(f"\tNeatly formatted name: {formatted_name}.") ``` To make sure `get_formatted_name` function properly, using `unittest` module of Python ```python import unittest from name_function import get_formatted_name class NamesTestCase(unittest.TestCase): """Test for name_function.py""" def test_first_last_name(self): """Do names like 'Janis Joplin' work?""" formatted_name = get_formatted_name('janis', 'joplin') self.assertEqual(formatted_name, 'Janis Joplin') if __name__ == '__main__': unittest.main() ``` Key points: * Import `unittest` module as shown * Import function to be tested * Here, `unittest`'s most useful feature `assert` is used. `assertEqual` can compare two objects * `__name__` is a **special variable**, which is set when the program is executed. If the file is being run as the main program, the value of `__name__` is set to `'__main__'` Result of running this test: ``` . ---------------------------------------------------------------------- Ran 1 test in 0.000s OK ``` * First dot represent the single test passed. ### A Failing Test Failure case returns ``` F ====================================================================== FAIL: test_first_last_name (__main__.NamesTestCase) Do names like 'Janis Joplin' work? ---------------------------------------------------------------------- Traceback (most recent call last): File "test_name_function.py", line 10, in test_first_last_name self.assertEqual(formatted_name, 'Janis Joplins') AssertionError: 'Janis Joplin' != 'Janis Joplins' - Janis Joplin + Janis Joplins ? + ---------------------------------------------------------------------- Ran 1 test in 0.001s FAILED (failures=1) ``` Highlight: * `FAIL: test_first_last_name` tell's where the error coming from. ## Fix them If employing TDD, we can first create test case, then develop code to be test against. ## Testing a Class e.g. above shown how to write test case for single function, we also need to write test for class. ### A Variety of Assert Methods Python `unittest` module provide various assert methods in `unittest.TestCase` class. List of available assert methods | Method | Use | | ------------------------- | -------------------------------- | | `assertEqual(a,b)` | Verify `a == b` | | `assertNotEqual(a,b)` | Verify that `a != b` | | `assertTrue(x)` | Verify that x is True | | `assertFalse(x)` | Verify that x is False | | `assertIn(item, list)` | Verify that item is in list | | `assertNotIn(item, list)` | Verrify that item is not in list | ### A Class to Test Steps to write test case: 1. Importing `unittest` module and class to test 2. Creating a test class to inherit from `unittest.TestCase` 3. Creating test function under class 1. Create an instance of the class to be tested. 2. Verify its operation e.g. * class to be tested in [survey.py](src/chap11/survey.py) have class `AnonymousSurvey` * script to use class [language_survey](pcc_2e/chapter_11/language_survey.py) * test script as shown below: ```python import unittest from survey import AnonymousSurvey class TestAnonymousSurvey(unittest.TestCase): """Tests for the class AnonymouseSurvey""" def test_store_single_response(self): """Tests that a single response is stored properly""" question = "What language did you first learn to speak?" my_survey = AnonymousSurvey(question) my_survey.store_response('English') self.assertIn('English', my_survey.responses) def test_store_three_responses(self): """Test that three individual responses are stored properly""" question = "What language did you first learn to speak?" my_survey = AnonymousSurvey(question) responses = ['English', 'Spanish', 'Mandarin'] # Store responses for response in responses: my_survey.store_response(response) # Test my_survey operation for response in responses: self.assertIn(response, my_survey.responses) if __name__ == '__main__': unittest.main() ``` We can add more test functions to test different scenario. But it's too repetitive. Use `setUp()` ### The setUp() Method We can use `setUp()` to setup initial states of `self.my_survey`, which can reduce repetitions. ```python import unittest from survey import AnonymousSurvey class TestAnonymousSurvey(unittest.TestCase): """Tests for the class AnonymouseSurvey""" def setUp(self): """ Create a survey and a set of responses for use in all test methods. """ question = "What language did you first learn to speak?" self.my_survey = AnonymousSurvey(question) self.responses = ['English', 'Spanish', 'Mandarin'] def test_store_single_response(self): """Test test a single response is stored properly""" self.my_survey.store_response(self.responses[0]) self.assertIn(self.responses[0], self.my_survey.responses) def test_store_three_responses(self): """Test that three individual responses are stored properly.""" for response in self.responses: self.my_survey.store_response(response) for response in self.responses: self.assertIn(response, self.my_survey.responses) if __name__ == '__main__': unittest.main() ``` * Use `setUp()` can make test methods easier to write. * We can make one set of instances and attributes in `setUp()` and then use these instances in all your test methods. Note of `unittest` * When a test case is running, Python prints one character for each unit test as it's completed. * A passing test prints a dot `.` * A test that results in an error prints an `E` * A test that results in failed assertion prints an `F`. * Hence, we can see a different number of dots and characters on the first line of output when you run your test cases.