How does Python perform unit testing?

foreword

In my day job, I'm a professional programmer. I use c++, c# and Javascript. I'm part of a development team that uses unit tests to verify that our code works the way it should.

In this article, I will examine how to create unit tests using Python by discussing the following topics.

· Unit testing basics

·  Available Python testing frameworks

·  Test Design Principles

·  Code coverage

Unit Testing Basics

I created unit test example using FizzBuzz coding style. Coding types is an exercise for the programmer. In this exercise, a programmer tries to solve a specific problem. But the main goal is not to solve problems, but to practice programming. FizzBuz is a simple code type that is great for explaining and demonstrating unit testing in Python.

unit test

Unit tests are automated tests written by programmers to test a small portion of a program. Unit tests should run fast. Tests that interact with the file system, database, or network are not unit tests.

To create my first FizzBuzz unit test in Python, I defined a class that inherits from unittest.TestCase. The unittest module is available in a standard installation of Python.

 import unittest
  class FizzBuzzTest(unittest.TestCase):
      def test_one_should_return_one(self):
          fizzbuzz = FizzBuzz()
          result = fizzbuzz.filter(1)
          self.assertEqual('1', result)
      def test_two_should_return_two(self):
          fizzbuzz = FizzBuzz()
          result = fizzbuzz.filter(2)
          self.assertEqual('2', result)

The first test case verifies that the number 1 passes the FizzBuzz filter, which returns the string '1'. Use self to verify the result. assertEqual method. The first parameter of the method is the expected result, and the second parameter is the actual result.

test case

We call the test_one_should_return_one() method in the test case FizzBuzzTest class. A test case is the actual test code that tests a specific part of the program.

The first test case verifies that the number 1 passes the FizzBuzz filter, which returns the string '1'. Use self to verify the result. assertEqual method. The first parameter of the method is the expected result, and the second parameter is the actual result.

If you look at these two test cases, you'll see that they both create an instance of the FizzBuzz class. The first is on line 6 and the other is on line 11.

We can improve the code by refactoring the creation of the FizzBuzz instance from these two methods.

import unittest
  class FizzBuzzTest(unittest.TestCase):
      def setUp(self):
          self.fizzbuzz = FizzBuzz()
      def tearDown(self):
          pass
      def test_one_should_return_one(self):
          result = self.fizzbuzz.filter(1)
          self.assertEqual('1', result)
      def test_two_should_return_two(self):
          result = self.fizzbuzz.filter(2)
          self.assertEqual('2', result)

We use the setUp method to create an instance of the FizzBuzz class. The setup of the TestCase base class is executed before each test case.

Another method tearDown is called after each unit test execution. You can use it to clean up or close resources.

Test Fixture

Method setup and teardown are part of the test fixture. Test fixtures are used to configure and build the unit under test. Each test case can use these common conditions. In this example, I use it to create an instance of the FizzBuzz class.

To run unit tests, we need a test runner.

test runner

The test runner is the program that executes all unit tests and reports the results. Python's standard test runner can be run on the terminal using the following command.

python -m unittest test_fizzbuzz.py

test suite

The last term in the unit testing glossary is test suite. A test suite is a collection of test cases or test suites. Usually a test suite contains test cases that should be run together.

unit test design

Test cases should be well designed. The title and structure of the exam are the most important.

test case name

The name of the test is very important. It acts like a heading that summarizes the content of the exam. It's the first thing you see if the test fails. Therefore, the name should clearly indicate which features do not work.

The list of test case names should read like a summary or a list of scenarios. This helps the reader understand the behavior of the unit under test.

Construct test case method body

A well-designed test case consists of three parts. The first part, arranges and sets up the objects to be tested. The second part, Act, practices the unit under test. Finally, the third part, the assertion, makes a claim about what should have happened.

Sometimes I add these three parts as comments in my unit tests to make it clearer. 

import unittest
  class FizzBuzzTest(unittest.TestCase):
      def test_one_should_return_one(self):
          # Arrange
          fizzbuzz = FizzBuzz()
          # Act
          result = fizzbuzz.filter(1)
          # Assert
          self.assertEqual('1', result)

A single assertion per test case

Although there may be many assertions in one test case. I always try to use a single assert.

The reason is, when an assertion fails, the execution of the test case stops. So you never know if the next assertion in your test case will succeed or not.

Unit testing with pytest

In the previous section, we used the unittest module. The default installation of Python installs this module. The unittest module was first introduced in 2001. Based on JUnit, the popular Java unit testing framework developed by Kent Beck and Eric Gamma.

Another module, pytest, is currently the most popular Python unit testing framework. It's more pythonic than the unittest framework. You can define test cases as functions instead of deriving from a base class.

Because pytest is not in the default Python installation, we use Python's package installer, PIP, to install it. pytest can be installed by executing the following command in terminal.

pip install pytest

Below I convert the first FizzBuzz test case to pytest.

def test_one_should_return_one():
      fizzbuzz = FizzBuzz()
      result = fizzbuzz.filter(1)
      assert '1' == result

There are three differences. First, you don't need to import any modules. Second, you don't need to implement a class and derive from a base class. Finally, you can use the standard Python assert method instead of a custom one.

test device

As you recall, the unit test module uses setUp and tearDown to configure and build the unit under test. Instead, pytest uses the @pytest.fixture attribute. In your test cases, you can use the name of the method decorated with this attribute as an argument.

The pytest framework wires them up at runtime and injects fizzBuzz instances into test cases.

 @pytest.fixture
  def fizzBuzz():
      return FizzBuzz()
  def test_one_should_return_one(fizzBuzz):
      result = fizzBuzz.filter(1)
      assert result == '1'
  def test_two_should_return_two(fizzBuzz):
      result = fizzBuzz.filter(2)
      assert result == '2'

 If you want to simulate the behavior of the unit test tearDown() method, you can use the same method to do so. Instead of using return, use the yield keyword. Then you can put the cleanup code after the yield.

@pytest.fixture
  def fizzBuzz():
      yield FizzBuzz()
      # put your clean up code here

pytest-mark

Flags are properties that can be used when testing various functions. For example, if you add skip markers to your test cases, the test runner will skip the tests.

@pytest.mark.skip(reason="WIP")
  def test_three_should_return_fizz(fizzBuzz):
      result = fizzBuzz.filter(3)
      assert result == 'Fizz'

pytest plugin ecosystem

pytest has many plugins that add extra functionality. As of the time I write this article, there are almost 900 plugins. For example, pytest-html and pytest-sugar.

pytest-html

pytest-html is a plugin for pytest that generates HTML reports for test results. This is useful when you are running unit tests on a build server.

pytest-sugar

pytest-sugar changes the default look and feel of pytest. It adds a progress bar and shows failed tests immediately.

Create a code coverage report

There are tools to create code coverage reports. This code coverage report shows what code your unit tests executed.

I use Coverage and pytest-cov to create code coverage reports. Coverage is a general package for measuring code coverage. The module pytest-cov is a plugin for pytest to connect to Coverage.

Both can be installed using pip.

pip install coverage
  pip install pytest-cov

After you have installed these two commands, you can use them to generate coverage reports. Run them in terminal or command line.

coverage run -m pytest
  coverage html

 The first generates coverage data. The second command converts the data into an HTML report. Coverage stores reports in the htmlcov folder on the file system.

If you open index.html in a browser, it will show an overview of the coverage of each file.

 If you select a file, it will display the screen below. Coverage adds an indication to source code showing which lines are covered by unit tests.

Below we see that our unit tests do not cover lines 12 and 16.

branch coverage metrics

Coverage also supports branch coverage metrics. With branch coverage, if you have a line in your program that jumps to more than the next line, coverage tracks whether those destinations are visited.

You can create a coverage report with branch coverage by executing the following command.

pytest——cov-report html:htmlcov——cov-branch——cov=alarm

I instruct pytest to generate an HTML coverage report with branch coverage. It should store the result in htmlcov. Instead of generating coverage reports for all files, I tell coverage to only use alarm.py. 

Finally, I would like to thank everyone who has read my article carefully. Reciprocity is always necessary. Although it is not a very valuable thing, you can take it away if you need it:

These materials should be the most comprehensive and complete preparation warehouse for [software testing] friends. This warehouse has also accompanied tens of thousands of test engineers through the most difficult journey, and I hope it can help you! Partners can click the small card below to receive

Guess you like

Origin blog.csdn.net/OKCRoss/article/details/131478774