Introduction

Get introduced to the concept of testing and learn why it is important in software development.

Skilled Python programmers agree that testing is one of the most important aspects of software development. Even though this chapter is placed near the end of the course, it is not an afterthought; everything we have studied so far will help us when writing tests. In this chapter, we’ll look at the following topics:

  • The importance of unit testing and test-driven development
  • The standard library unittest module
  • The pytest tool
  • The mock module
  • Code coverage

In the case study for this chapter, we’ll focus – no surprise – on writing some tests for the case study examples. We’ll start with some of the fundamental reasons why automated software testing is so important.

Why test?

Many programmers already know how important it is to test their code. Some people argue that testing is more important in Python code because of its dynamic nature; compiled languages such as Java and C++ are occasionally thought to be somehow safer because they enforce type checking at compile time. However, Python tests rarely check types. They check values. They make sure that the right attributes have been set at the right time or that the sequence has the right length, order, and values. These higher-level concepts need to be tested in any language. The real reason Python programmers test more than programmers of other languages is that it is so easy to test in Python.

The essence of testing

But why test? Do we really need to test? What if we didn’t test? To answer those questions, reflect on the last time you wrote any code. Did it run correctly the first time? Free of syntax errors? Free of logic problems? It’s possible, in principle, to type in code that’s perfect once in a while. As a practical matter, the number of obvious syntax errors that had to be corrected is an indicator that perhaps there are more subtle logic errors that also had to be corrected.

The necessity of testing

We don’t need a formal, separate test to make sure our code works. Running the program, as we generally do, and fixing the errors is a crude form of testing. Python’s interactive interpreter and near-zero compile times makes it easy to write a few lines of code and run the program to make sure those lines are doing what is expected. While acceptable at the beginning of a project, this turns into a liability that grows over time. Attempting to change a few lines of code can affect parts of the program that we haven’t realized will be influenced by the changes, and without tests, we don’t know what we broke. Attempts at redesigns or even small optimization rewrites can be plagued with problems. Furthermore, as a program grows, the number of paths that the interpreter can take through that code also grows, and it quickly becomes impossible or a crude manual test to exercise all of them.

To assure ourselves and others that our software works, we write automated tests. These are programs that automatically run certain inputs through other programs or parts of programs. We can run these test programs in seconds and cover far more potential input situations than one programmer would think to test every time they change something.

“Software features that can’t be demonstrated by automated tests simply don’t exist.”

—Extreme Programming Explained, Kent Beck

The role of automated tests

There are four main reasons to write tests:

  • To ensure that code is working the way the developer thinks it should
  • To ensure that code continues working when we make changes
  • To ensure that the developer understood the requirements
  • To ensure that the code we are writing has a maintainable interface

When we have automated tests, we can run them every time we change code, whether it is during initial development or maintenance releases. Testing can confirm that we didn’t inadvertently break anything when adding or extending features.

The last two of the preceding points have interesting consequences. When we write tests, it helps us design the API, interface, or pattern that code takes. Thus, if we misunderstood the requirements, writing a test can help highlight the misunderstanding. From the other side, if we’re not certain how we want to design a class, we can write a test that interacts with that class so we have an idea of the most natural way to confirm that the interface works. In fact, it is often beneficial to write the tests before we write the code we are testing.

There are some other interesting consequences of focusing on software testing. We’ll look at three of these consequences:

  • Using tests to drive development
  • Managing different objectives for testing
  • Having a consistent pattern for test scenarios

Get hands-on with 1200+ tech skills courses.