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 a nutshell
  • 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.

Write a testdescribes new behaviourfails, redWrite codejust enough to passpasses, greenRefactorclean up safelyrepeatNext testnext behaviour

How it works

TDD runs as a short loop that repeats for every small piece of behaviour.

The red, green, refactor cycle

  1. Red. Write a test for a feature that does not exist yet. Run it and watch it fail.
  2. Green. Write the simplest code that makes the test pass. Run all tests again.
  3. 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 typeWhat it checksSpeed
Unit testOne function or class on its ownVery fast
Integration testSeveral parts working together, such as code and a databaseSlower
Functional or end to end testA whole feature as a user would use itSlowest

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.

Exam tip

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.

Watch out

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.