FavoritePythonLibs #1
Test Driven Development (TDD) with PyTest
This is the first part of a series in which I want to introduce the libraries respectively frameworks I most often use, during my everyday Python projects. The possibly most indispensable framework I use is PyTest, for Test-driven development (TDD) in Python. During the following chapters I want to introduce the main concepts of TDD, why it is important and how you can utilize it in Python.
What the heck is TDD?
TDD is a programming approach in which -it is hard to believe- a test function is written for each function you are going to implement. In this context the initial idea behind TDD is the demand for the test function to be written before(!) the actual target function, the so called production code, is implemented. Nevertheless, if you ask me this is a matter of taste and to be honest, better you write the test after the production code, than not writing the test at all, right?
The idea is pretty simple: A testing method calls the target function with a set of appropriate parameters and formulates an assert about how the result should look like. If this requirement is not fulfilled, it can be assumed that something went wrong. Let me give you a quite simple example on this.
Let's assume we want to implement a function that calculates the sum of two integer values, called add_two_integers
with two parameters x
and y
.
According to the prior mentioned demand of writing the test before the production code, we start implementing an appropriate testing function that checks whether the parameters 1
and 1
return a correct value, using the target function.
def test_add_two_integers():
assert add_two_integers(1, 1) == 2
We will now run this test, although -or just because- we know it will fail, which is completely obvious, as we not yet implemented the target function.
platform darwin -- Python 2.7.15, pytest-3.5.0, py-1.5.3, pluggy-0.6.0
rootdir: /Users/localhostport80/Desktop/, inifile:
collected 1 item
test_add.py F [100%]
======= FAILURES =======
======= test_add_two_integers =======
def test_add_two_integers():
> assert add_two_integers(1, 1) == 2
E NameError: global name 'add_two_integers' is not defined
test_add.py:4: NameError
======= 1 failed in 0.05 seconds =======
Afterwards we will implement the respective production code.
def add_two_integers(x, y):
""" Simply adds to integer values.
Args:
x (int): The first value.
y (int): The second value.
Returns:
int: The result of x + y.
"""
return x + y
Running the same test again will result in a successful test.
======= test session starts =======
platform darwin -- Python 2.7.15, pytest-3.5.0, py-1.5.3, pluggy-0.6.0
rootdir: /Users/localhostport80/Desktop/, inifile:
collected 1 item
test_add.py . [100%]
======= 1 passed in 0.01 seconds =======
Congratulations! You successfully ran your first test
How does TDD work in Python?
This is now where PyTest joins the game. PyTest provides an easy interface to TDD, as explained above.
usage: pytest [options] [file_or_dir] [file_or_dir] [...]
positional arguments:
file_or_dir
What it actually does is, it recursively searches for testing modules in a given set of directories. Those modules are supposed to start with test_<Name of the module>
and each method within these modules, representing a testing method, should start with the prefix test_ and hence should look the like following test_<Name of the target method>
.
Running the tests again on a small project as shown in the above screenshot will result in an output as shown in the following.
======= test session starts =======
platform darwin -- Python 2.7.15, pytest-3.5.0, py-1.5.3, pluggy-0.6.0
rootdir: /Users/localhostport80/Desktop/MyProject, inifile:
collected 3 items
tests/test_misc.py .. [ 66%]
tests/test_tools.py . [100%]
======= 3 passed in 0.02 seconds =======
What kind of tests are there?
I will now sketch the idea behind the most important kinds of tests.
- Unit Tests: Testing the internal workings of a class. The important requirement in this context is that unit tests should represent stand-alone tests, which are not related to other resources.
- Integration Tests: Integration tests are quite comparable to unit tests, with the main difference of relying on external resources like for example databases or disk access. Integration tests are supposed to test the correct interworking of different components that depend on each other.
- Regression Tests: Imagine implementing new features or for example bug fixes. Regression tests are used in this context in order to make sure that these new features do not break existing features, by re-testing scenarios which definitely had worked in the past.
- System Tests: System tests are testing the target system as a black box. The whole system is tested as a whole, in order to verify that the system meets its requirements. If there exist dependencies to other systems, those are supposed to be mocked up, which trivially means that they are emulated, such that each system test focuses on exactly a single system.
- Acceptance Tests: These tests represent the final level of testing in order to determine whether the system is ready for release. The user will test the system to find out whether or not the application meets their business’ needs and fulfils the predefined requirements.
Final Words
From my point of view TDD is a great approach that helps you to stay in the track. Especially in huge projects with hundereds or thousands of methods and modules -I think every programmer will agree on this- refactoring is a challenging task. With TDD at any point in time you can easily make sure every part of your software is working, as it is supposed to.
Congratulations @localhostport80! You have completed some achievement on Steemit and have been rewarded with new badge(s) :
Click on the badge to view your Board of Honor.
If you no longer want to receive notifications, reply to this comment with the word
STOP