This Python Library Will Help You Build Scalable Data Science Projects

Pytest: A Testing Framework for Python Code

Photo by Ratanjot Singh on Unsplash

How can you check that your code changes actually achieve what they’re meant to?

Ensuring your code has integrity is actually quite difficult to ensure, especially at scale. Usually you’ll work in a large team with different people working on different parts of the system. Everyone is tinkering about with something and if you’re using an agile methodology, you’ll be committing code multiple times a day. So how can you keep track that all your changes are backwards compatible? How can you keep track that your code changes maintain at least the same functionality as the code you’re removing (without the bad bits)?

You can test code in a number of ways. A lot of it tends to be sensibility testing and coming up with situations or extreme cases in which the code will definitely fail, and then by narrowing the scope.

It’s a long process, but this is so important because some code holds a lot of responsibility: at times, faulty code can bring down the company.

That’s not a joke, check these stories:

  1. GETCO lost $400m in Trading caused by Computer Error (and was rescued through acquisition by KCG)
  2. Y2K Bug that reportedly cost the industry upwards of $300bn to resolve (that’s billion with a b!).
  3. AT&T Goes does for 9 hours and 75 million calls go unanswered — what caused it? A software update

These stories broke headlines and broke companies but just think about it as a customer as well: would you use an application that was super buggy? No, I wouldn’t either.

The Python community has appreciated testing for a while and pretty much all developers should know how to test their code. In what follows, we’ll be discussing the library pytest.

Photo by beuwy.com Alexander Pütter on Unsplash

So what is pytest?

Pytest a framework that makes it easy to write small tests, yet scales to support complex functional testing for applications and libraries.

Pytest has been built over a number of years and it’s been so popular for the following reasons:

  1. easy and simple syntax
  2. run specific tests or a subset of tests in parallel
  3. built-in automatic detection of tests and the ability to skip tests
  4. encourages test parametrisation and gives useful information on failure
  5. encompasses minimal boilerplate
  6. makes testing easy by providing special routines and extensibility (many of plugins, hooks etc. are available)
  7. open-source i.e. allows contribution from the larger community

These are but a few points that make pytest easy to use. Note that testing also forms an integral part of your continuous integration and continuous development process, but this will be covered elsewhere.


Getting Started with PyTest

To install pytest, open up your command line and run the following command:

pip install pytest

You can also check whether the version is installed correctly with:

$ pytest — version

Your First Test

Before getting started, create a folder with name “Sample Test” and make a python file called testing_sample.py.

The assert statements are used to ascertain a true or false status in a method or test expectation.

Now in what follows, we’ll produce your first test in just 4 lines of code:

# testing_sample.py
def func(y):
return y + 1
def test_answer():
assert func(2) == 4

On execution of the above test function with:

$ pytest testing_sample.py

It would return a failure report because func(2) is not equal to 4 (i.e. 3!= 4). Additionally, it provides you with a solution for test failure. However, if you reset the code to: func(2)==3, this would then pass as it isTrue. Make sure to correct this before progressing.

Note: with standard test discovery rules, you can store multiple tests of your files in your current directory and its subdirectories and pytest will run through them all

Assert Exception

To determine if a piece of code causes an exception, you can create a test with an assert with the raise helper as shown:

# testing_sysexit.py
import pytest
def p():
raise SystemExit(1)
def test_mytest():
with pytest.raises(SystemExit):
p()

This test would result in an exception being thrown but as it’s expected (and controlled using the raise keyword), the test would pass and continue as on.

Multiple Tests Class Grouping

Now if you want to run multiple tests, you can choose to group them as a class as such:

# testing_class.py
class TestClass:
def test_one(self):
y = “this”
assert “h” in y
def test_two(self):
y = “hello”
assert hasattr(y, “check”)

Pytest discovers every test in the class bearing prefixes like “testing” as used. we can now run the code with:

$ pytest testing_class.py

You can discover that the first test passed while the second failed to give the reasons for the failure to gain a proper understanding.

FIXTURES

pytest fixtures are decorator functions, which essentially run before you run a function. A pytest fixture is implemented in the same manner as a decorator function as follows:

@pytest.fixture
def abc(input):
...

The implementation in pytest offers dramatic improvements over the classic xUnit style of setup/teardown functions because primarily, fixtures have explicit names and are activated by declaring their use from test functions. Fixtures are also modular, so each fixture name triggers a fixture function which can itself use other fixtures. Finally, fixture management scales from a simple unit test to more complex parametrised fixtures. With high-grade production code in a big organisation, the ability to configure and re-use fixtures is imperative.

In more layman terms, fixtures are generally used to set the data for a test by e.g. setting a database connections or connecting to a URL to test: generally, setting some sort of input data. So instead of running the same code for every test, we can attach fixture function to the tests and it will run and return the data to the test before executing each test.

Let’s look at a ream example. Create a file test_div.py and add the below code to it

# test_div.py
import pytest
@pytest.fixture
def input_value():
input = 39
return input
def test_divisible_by_3(input_value):
assert input_value % 3 == 0
def test_divisible_by_6(input_value):
assert input_value % 6 == 0

When running this file, we will get a pass in the first test but a fail in the second test as 39 is not divisible by 6.

Now the approach comes with its own limitation. A fixture function defined inside a file can only be used within that file only (as it’s in that scope). To make a fixture available to multiple test files, we have to define the fixture function in a file called conftest.py.


The coding community feels strongly about testing because it really does save a lot of time and hassle when you’re making changes as part of a wider web of code. We’ve all been in that case where you make a small change and suddenly everything breaks and there’s no clear reason why. Testing ensures that we stay on-top of all problems and can isolate/fix them quickly.

I’d recommend it as a practitioner because testing allows you to build a solid foundation to any new framework. You can at times test to death, but, it’s better than not testing at all. After all, you want to ensure that your code has the highest degree of integrity as possible — not only for when you merge it, but for there after.


Thanks for reading again!! Let me know if you have any questions and I’ll be happy to help.

Keep up to date with my latest work here!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Powered by WordPress.com.

Up ↑

%d bloggers like this: