Keenesystem

A better approach to understanding Test Driven Development (TDD) and Automated testing

Armstrong Ebong

--

Topics covered

  • TL;DR
  • Introduction
  • Why write tests before codes
  • What is Python doc-test
  • Problem statement
  • Writing Python doc-test
  • Run to fail
  • Writing code
  • Run to pass
  • Conclusion

TL;DR

1. Write test before writing code.

2. Run test — No code implementation means that all the tests will fail.

3. Write code implementation to fix failed tests

4. Rerun test and ensure that all tests passes

5. If any test fails, repeat step 3

The importance of writing test in software engineering today cannot be over-emphasized. It’s also one of the most underrated concepts of software engineering. Over the last 5 years as a software engineer, I have seen and I do know just how relevant testing is for any application in development or in production. In this article, I will show you one of the easiest ways to practice Test Driven Development (TDD) and write automated tests in Python.

Oh! it’s gonna be fun as well.

Note: This article was written using Python . However, the method used can be utilized in any programming language. This article is written with the assumption that you understand basic Python data types and data structure. We will not be covering these topics. I hope that what you will learn here can help you understand TDD and automated testing better

If you are like me, who learns by doing and not reading alone, then maybe you might relate to this narrative I am about to share. As someone who is curious and loves to engage in conversations related to tech, I have over the years learned to learn. I see people in tech as free spirits — They learn with no restriction, thanks to the internet. There’s virtually nothing that you cannot find in the internet these days. Whatever you learn in tech is usually not necessarily tied to an area of application and that is the fun part. But, there’s also the part where we are very often bombarded with too much information about emerging technologies, concepts, ideologies, principles, industry standards etc. This can be overwhelming to say the least.

Test Driven Development is an approach to writing test that encourages developers to first and foremost clearly write down test cases for the features and functionalities they wish to build based on the scope and requirements of the software before building the feature. The approach of TDD is to write code that when run against the test cases, fails at first. Then write code that make the failed tests pass, repeating this step for failed test. That’s TDD in a nutshell.

As humans and engineers, we often have to learn and relearn concepts as we develop ourselves. Surely, we can agree that we’ve had those days where we sit back, contemplate and decide to take a break, daydreaming of how we became the next Elon Musk, Evan You etc. building the next big thing and revolutionizing the way the internet works. :) We all do!

Too often, when in this state of mind, I usually remind myself of why I do what I do in the first place. Anyone who learns anything has to pass through the ritual of self doubt as we pursue knowledge. So you are not alone.

I wasn’t used to TDD. I mean in reality when working on real projects, planning to write tests for every function — helpers, libraries etc does feel like a time consuming activity or chore and it is, but its not wasted time. Most times, when within an agile environment, all details are usually not initially available. So there’s a lot of figuring out to do.

Learning TDD was like that for me. I read books about TDD, but I just couldn’t understand the concept even when I tried to practice it, I just didn’t get it. I tried for a while, but later left it to face other pressing matters and to take some break. Few years later, I found myself writing code with a lot of test cases which I honestly didn’t like writing, but I knew that writing them would allow me to sleep well at night. Soon after, I starting thinking about TDD again. Wondering if I would enjoy writing test using the TDD approach. Read on as I explain the approach I used in learning TDD.

# Why write tests before codes

The day I understood the TDD approach to software testing, I wasn’t trying to learn it. It came naturally. You may be wondering how. I’ll tell you how. When I started to write automated tests for a Python module using Python doc-test. I used a text file to write the test for the python module. After carefully reading through the API docs of how to write python doc-test. I found myself replicating the implementation instructions again into the text file. I first wrote down the test cases and their expected values, doing my best to leave behind good documentation as well. This is what I recommend.

Once I was done, confident that I had covered most of the test scenarios. I started writing code to fix the failed test cases. It soon dawned on me that I was already practicing TDD without even knowing. TDD approach to writing test is to write tests that fail at first. Then later writing code that makes the failed tests pass. To achieve this, I recommend that you do not focus on the code implementation before the test. If tests are written without a code implementation, that obviously means that all the tests will fail. Once all your tests have failed, you can then start implementing code to make the tests pass.

This approach forces you to practice TDD conveniently. This forces the human mind to do the obvious, that is; fix all the failed test cases. Using this approach can make writing test fun and productive. Practicing TDD shouldn’t be difficult. Detail is king in testing, however, knowing the difference between over-thinking and being detailed-oriented is also key. This article was written with the hope that it would help others like me ( including over-thinkers) who are finding it difficult to grasp the concept of TDD understand it better. In the next section , I will use an example to show you how you can practice TDD using Python doc-test. If you are a developer, I am sure you would love it or maybe relate more.

# What is Python Doc-Test?

Python Doc-test allows you to write automated tests as part of the documentation of a module in python.

The way a python doc-test works is by first parsing the document text file. Then it runs the commands, automate the test, and lastly compares the output results with the expected values. If there are any failed tests, it notifies you. Python doc-test has some APIs that you can learn to do advanced operations, but we won’t be going into the details of that, so we will focus on the basics to drive home the concept of TDD. The Python doc-test is very similar to a python interactive interpreter, see below. You can check out the Python doc-test API here.

Example of a function written in a doc-test file
Example of a function written on Interactive Python Interpreter

From the screenshots, you will notice some similarities especially the >>> symbol. The following sections will explain further.

# Problem statement

Every solution has a problem. The problem we will be considering is as follows:

Write a function that adds 2 integers.

- Prototype: `def add_integer(a, b=98):`
- `a` and `b` must be integers or floats, \
otherwise raise `a` `TypeError` exception with the message \
`a must be an integer` or `b must be an integer`

- `a` and `b` must be first casted to integers if they are float
- Returns an integer: the addition of `a` and `b`
- You are not allowed to import any module

Simple enough, right? Let’s just jump right in and I will show you how I would approach this problem writing automated test using Python doc-test.

# Writing Python doc-test

The first thing I recommend after you have understood a problem statement is to write test cases. So create a folder in the root of your project. Call the folder tests.

Project structure

We will have a file named add_integers.txt where we will write our function definition. The test file will be inside a file named test_add_integers.txt inside the tests folder. Inside the test_add_integers.txt file, one could write the following test cases:

From the above, we have created 8 test cases. You could create more, but for this article, we will only be using 8 test cases. In a Python doc-test, the >>> symbol is similar to a prompt in the interactive python interpreter we talked about. You simply write the command you wish to run. Then immediately following on a newline, you indicate the expected output of the command. For example, on line 4, I want to call the function with the arguments 2 and 5. Then on line 14, I write the expected value, which is 15. As we will see later, Doc-test will parse the text file and anywhere the >>> symbol is encountered, it will run the command and compare the expected result with the actual program output.

# Run to fail

You can run a Python doc-test using wildcards or specific file paths. Python doc-test module can be run as a terminal command or as a Python script. We will be running it as a terminal command as follows:

$ python3 -m doctest ./tests/*.txt
# OR
$ python3 -m doctest ./tests/test_add_integer.txt

The aim here is to run and make sure that all the tests fail. You should get the following output:

Failed test cases

I omitted the rest of the output except for the summary of the failed test cases (screenshot above). If you’re following along, you should have a similar output.

# Writing code

Next thing after writing test is to code. The following are the contents of the file, add_integer.txt:

# Run to pass

Next, run the doc-test again on terminal, using the following command

$ python3 -m doctest ./tests/*.txt
# OR
$ python3 -m doctest ./tests/test_add_integer.txt

At this stage, we want all test to pass. If a test fails you will have to revisit and refactor the code and rerun the test again, in that order. You will keep doing this until all the test passes. After you run the command, if all your test pass, you will notice that nothing shows. This is an indication that all your tests passed successfully. If there was a failed test, you will get an output showing the error. If you want to see an output regardless of whether the test passes or not, you can pass the -v argument to the command to show verbose output like this.

$ python3 -m doctest ./tests/test_add_integers.txt  -v

# Conclusion:

Notice the approach I use to practice TDD. I first understand the problem statement and also figure out the test cases to write. Then I write test for those scenarios. After writing the tests, I code the solution, working towards fixing all the failed tests. When you follow this approach you would have to write your test to fail initially and then after you can start writing code to make the test pass. This is the practice of TDD in a nutshell.

Finally, when learning, let your curiosity guide you. I’ve found out that you need no extra motivation when your curiosity drives you. If you like, please share and give a clap, thumbs up or like. To read more articles like this, subscribe to my newsletter by following me. Stay informed.

Peace!

--

--

Armstrong Ebong
Armstrong Ebong

Written by Armstrong Ebong

I share tips and tricks to get shit done. I do my best to simplify complex concept and provide succinct clarity through writing

No responses yet