Using pytest and fixtures

An alternative to the unittest runner is the pytest test runner. The pytest framework has an excellent test discovery feature that goes beyond what the unittest tool can discover.

The unittest runner can be used in the following two ways.

  • With a test suite object. The previous examples have focused on this.
  • To search for classes that are extensions of unittest.TestCase, build a test suite from those, and then run the suite. This offers considerable flexibility. We can add test cases without also having to update the code to build the test suite.

The pytest tool can also locate unittest.TestCase class definitions, build a suite of tests, and execute the tests. It can go beyond this and also locate functions with names starting with test_ in modules where the name starts with test_. Using simple functions has some advantages over the more complex unittest.TestCase class definitions.

The primary advantage of using separate functions is the resulting simplification of the test module. In particular, when we look back at the TestCardFactory example, we see that there is no setUp() required for the tests within the class. Because all of the methods are independent, there's no real need to bind these methods into a single class definition. Even though this is a book on object-oriented Python, there's no reason to use class definitions when they don't improve the code. In many cases, class-oriented test case definition isn't helpful and separate functions executed by pytest have advantages.

The pytest approach leads to the following two other consequences:

  • The self.assert...() methods are not available. When using pytest, the Python assert statement is used to compare expected results with actual results.
  • The stateful class variables used by setUp() and tearDown() aren't available. In order to set up and tear down test contexts, we'll use the pytest @fixture functions.

As a concrete example of the simplifications possible, we'll review some examples from earlier, starting with tests for the Card class. The pytest version is as follows:

def test_card():
three_clubs = Card(3, Suit.CLUB)
assert "3♣" == str(three_clubs)
assert 3 == three_clubs.rank
assert Suit.CLUB == three_clubs.suit
assert 3 == three_clubs.hard
assert 3 == three_clubs.soft

The function's name must begin with test_ to be sure pytest can discover it. The test setup creates a card instance, and a number of assert statements to confirm that it has the expected behavior. We don't have quite as much overhead when using functions with the assert statement as we do when using unitest.TestCase subclasses.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset