PyCharm 2019.1 Help

Pytest

PyCharm supports pytest, a fully functional testing framework.

The following features are available:

By default, the suggested default test runner is unittest. So, to utilize pytest, you need to make it the default test runner first.

Enable Pytest for you project

  1. Open the Settings/Preferences | Tools | Python Integrated Tools settings dialog as described in Choosing Your Testing Framework.

  2. In the Default test runner field select pytest.

  3. Click OK to save the settings.

Now, that pytest is set as the default testing framework, you can create a small test for the Car sample. Let's create a pytest test to check the brake function.

Create a test

  1. Create a Python project.

  2. On the main menu, click File | New, choose Python file, type Car.py, and click OK.

  3. Copy and paste the Car sample into the Car.py file.

  4. Create one more Python file and name it test_car_pytest.

  5. Add the following code to the newly created file. This code sets the initial speed value of Car to 50 and checks if speed gets properly set to 45 after the brake() function execution.

from Car import Car def test_car_brake(): car = Car(50) car.brake() assert car.speed == 45

Note that PyCharm recognizes the test subject and offers completion for the Car class' instance.

Autocompletion for the test subject

PyCharm detects a function with an assert statement and adds the Run Test icon to the gutter.

Although Go To Test Subject and Go To Test commands of the context menu are not supported for pytest, you can navigate to the tested code in Car.py by using the Go To Declaration (Ctrl+B) command.

Run a test

  1. Click Run Test to run the test:

    Run the test

    Note that PyCharm automatically creates a pytest Run/Debug configuration:

    Suggested run/debug configuration for pytest

    Select Run pytest for test_car_pytest to execute the test.

  2. Inspect test results:

    Pytest run

  3. Alter the assert statement to the following: assert my_car.speed == 4599.

  4. Rerun the test to evaluate the assert failing report:

    Assert failing report
    Note that pytest provides an explicit report on the failure.

With pytest fixtures you can create small test units that can be reused across the testing module. All you need is to mark a reusable unit with @pytest.fixture.

Use fixtures

  1. Modify your Car pytest test as follows:

    import pytest from Car import Car @pytest.fixture def my_car(): return Car(50) def test_car_accelerate(my_car): my_car.accelerate() assert my_car.speed == 55 def test_car_brake(my_car): my_car.brake() assert my_car.speed == 45

    my_car() is a fixture function that creates a Car instance with the speed value equal to 50. It is used in test_car_accelerate and test_car_brake to verify correct execution of the corresponding functions in the Car class.

    Note that the my_car fixture is added to the code completion list along with other standard pytest fixtures, such as tempdir.

    Autocompletion for a pytest fixture

  2. Click either of the Run Test icons, or run the entire test module.

You can enable sharing fixture instances across tests using the scope parameter of the fixture function. For more information about pytest fixtures, see pytest fixtures documentation.

You might want to run your tests on the predefined set of data. PyCharm supports test parametrization implemented in pytest through @pytest.mark.parametrize.

Apply parametrization

  1. Let us create a set of speed values to test car.accelerate and car.brake functions: speed_data = {45, 50, 55, 100}

  2. Modify the test code to the following:

    import pytest from Car import Car speed_data = {45, 50, 55, 100} @pytest.mark.parametrize("speed_brake", speed_data) def test_car_brake(speed_brake): car = Car(50) car.brake() assert car.speed == speed_brake @pytest.mark.parametrize("speed_accelerate", speed_data) def test_car_accelerate(speed_accelerate): car = Car(50) car.accelerate() assert car.speed == speed_accelerate

    Note that PyCharm detects the newly created parameters and adds them to the completion list.

    Code completion for pytest.mark.parametrize
  3. Run the test for the car.brake() function. You should expect the following test report:

    Three tests failed and one test passed for the car.brake function

You can also run the test for car.accelerate() function to ensure that it fails for all speed values but 55. Refer to pytest documentation for more information about parametrized tests.

In addition to the mentioned capabilities, pytest supports Behavior-Driven Development (BDD) through pytest_bdd. This is particularly helpful when you need to quickly record your test using the Gherkin language and utilize beneficial features of pytest, such as fixture.

Implement test scenarios

  1. Let us modify the Car sample to validate the car's speed. Add the following function to the Car.py file:

    def speed_validate(self): return self.speed <= 160
  2. Next, enable pytest-bdd in your project. In the Settings/Preferences dialog (Ctrl+Alt+S), navigate to Languages & Frameworks | BDD, and from the Preferred BDD framework list select pytest-bdd.

  3. Create a .feature file to record BDD scenarios using the Gherkin language. Right-click the project root and select New | Gherkin feature file. In the opened dialog, specify car.feature as the filename and click OK.

  4. Add the following scenarios to the car.feature file:

    Feature: Speed Scenario: Valid speed Given Speed is less than 160 When Accelerated Then Speed is valid Scenario: Invalid speed Given Speed is more than 160 When Accelerated Then Speed is invalid

    Both scenarios validate the speed of the car. The speed is supposed to be valid when it does not exceed the value of 160. Note the scenario steps are highlighted because they are not defined by this moment.

    bdd scenarios
  5. PyCharm enables quick generation of step definitions for the entire feature file. Set cursor on any of the highlighted steps, click Ctrl+Shift+A, and select Create all step definitions. In the opened dialog, specify the name of the test file (it should start with test), select Python (pytest-bdd) from the File type list, and, if needed, modify the default file location.

    step definitions generation

    Inspect the test_car_pytest_bdd_fixture.py file. It contains all required import statements and definitions for each scenario steps.

  6. Let us modify the test file to use a pytest fixture for a Car object and to add more test logic:

    from pytest_bdd import scenario, given, when, then import pytest from Car import Car @pytest.fixture def my_car(): return Car() @scenario('car.feature', 'Valid speed') def test_speed_valid(): pass @scenario('car.feature', 'Invalid speed') def test_speed_invalid(): pass @given("Speed is less than 160") def set_valid_speed(my_car): my_car.speed = 50 @given("Speed is more than 160") def set_invalid_speed(my_car): my_car.speed = 100 @when("Accelerated") def car_accelerate(my_car): my_car.accelerate() @then("Speed is valid") def success(my_car): assert my_car.speed_validate() @then("Speed is invalid") def fail(my_car): assert not my_car.speed_validate()
  7. Run the test by creating the corresponding Run/Debug configuration. You can also run either of the scenarios using the Run icon (Run icon) in the gutter.

  8. Inspect the test run results.

    pytest-bdd results

    In our example, we have the test_speed_valid test passed and the test_speed_invalid test failed.

Last modified: 17 July 2019

See Also