Software Development and Design · Topic 3 of 7
Test Driven Development
Understand test driven development, the red green refactor cycle, and how unit, integration and functional tests differ, with a short Python example.
- 4 min
- In TDD you write the test first, and it fails at first.
- The loop is red, green, refactor.
- Write only enough code to make the test pass.
What it is
Test driven development, or TDD, is a way of working in which you write a failing test before you write the code that makes it pass.
The test describes what the code should do. The code is then written to meet that description, and nothing more.
The aim is code that is correct from the start, easy to change and covered by tests. Each test also acts as a small piece of documentation.
How it works
TDD runs as a short loop that repeats for every small piece of behaviour.
The red, green, refactor cycle
- Red. Write a test for a feature that does not exist yet. Run it and watch it fail.
- Green. Write the simplest code that makes the test pass. Run all tests again.
- Refactor. Tidy the code and remove duplication. Run the tests to prove that nothing broke.
The failing first run matters. It proves the test can actually fail, so a later pass means something.
A small example
Suppose you need a function that checks whether a VLAN ID is valid. You write the test first, using the standard unittest module.
import unittest
from vlan import is_valid_vlan
class TestVlan(unittest.TestCase):
def test_valid_range(self):
self.assertTrue(is_valid_vlan(100))
def test_zero_is_invalid(self):
self.assertFalse(is_valid_vlan(0))
def test_too_high_is_invalid(self):
self.assertFalse(is_valid_vlan(4095))
if __name__ == "__main__":
unittest.main()
At first the module vlan does not exist, so the tests fail. You then write the smallest function that passes them.
def is_valid_vlan(vlan_id):
return 1 <= vlan_id <= 4094
Later you might refactor the function or add tests for text input. The tests tell you at once if you break something.
Types of test
| Test type | What it checks | Speed |
|---|---|---|
| Unit test | One function or class on its own | Very fast |
| Integration test | Several parts working together, such as code and a database | Slower |
| Functional or end to end test | A whole feature as a user would use it | Slowest |
TDD is built mainly on unit tests, because they are quick enough to run after every small change.
Test doubles
A unit test should not depend on a real network device or a live API. You replace the outside dependency with a fake object, often called a mock.
A mock returns a prepared answer, so the test runs fast and gives the same result every time. Python provides unittest.mock for this purpose.
Benefits and limits
- Bugs are found early, when they are cheap to fix.
- The design tends to be simpler, because you write only code that a test demands.
- You can refactor with confidence, because the test suite catches regressions.
- Tests run automatically in a CI pipeline on every change.
TDD takes more time at the start. The tests also need maintenance, and poor tests give false confidence.
Where tests fit in automation
Network automation code is still code. A script that builds a configuration or parses a device reply needs tests like any other program. A test can feed in a saved sample reply and check the result.
In an exam question about the order of steps, remember that the test comes first and fails first. Only then do you write the code.
What to remember for the exam
Six facts summarise this topic.
- In TDD you write the test first, and it fails at first.
- The loop is red, green, refactor.
- Write only enough code to make the test pass.
- Unit tests check one small part. Integration tests check parts working together.
- Mocks replace real dependencies such as devices and APIs.
- Passing tests give confidence to refactor, and CI can run them automatically.
Common mistakes
Four mistakes are typical.
Writing the code first. If the tests come afterwards, you are not doing TDD, and the tests tend to copy the code rather than the requirement.
Skipping the refactor step. The code passes but becomes messy, and the cleanup never happens.
Writing huge tests. A test that checks many things is hard to read and hard to diagnose when it fails. Keep each test small and focused.
Testing against a live device. Tests that need real equipment are slow and unreliable. Use mocks for unit tests.
A passing test suite is only as good as its tests. Passing tests do not prove that there are no bugs.
Check yourself
Work through these three questions.
What is the order of steps in the TDD cycle?
Red, green, refactor. You write a failing test, make it pass with simple code, then clean the code up.
Why should you watch the new test fail before writing the code?
It proves the test can fail, so a later pass shows that the new code works.
Why use a mock in a unit test of code that calls an API?
A mock gives a prepared answer, so the test is fast and repeatable and does not need the real service.
Topic 3 of 7 in Software Development and Design
Test yourself on Software Development and Design
The full Cisco CCNA 200-901 DEVASC question bank has 808 questions, with timed practice exams and an explanation for every answer.
This study guide is independent and is not affiliated with, endorsed by, or sponsored by Cisco. Vendor names, certification names and exam codes are trademarks of their respective owners and are used for identification only.