PyCharm 2023.3 Help

Test your first Python application

Remember, in the first tutorial you’ve created your first Python application, and in the second tutorial you’ve debugged it. Now it’s time to do some testing.

Choosing the test runner

PyCharm auto-detects a test runner that is installed on your Python interpreter and uses it to run tests. If no specific test runner is installed, PyCharm uses unittest.

To explicitly set the required test runner in the project settings, press Control+Alt+S to open the IDE settings and select Tools | Python Integrated Tools, and then select the target test runner from the Default test runner list.

Selecting a test runner

For more information, refer to Testing frameworks.

Creating tests

A quick way to create tests is to have PyCharm stub them out from the class we’d like to test. To do this, we need to open car.py, then right-click the name of the class, point to Go To, and then choose Test (or just press Control+Shift+T):

Go to test

A popup appears that suggests creating a new test:

Create a new test

OK, let’s do it. We are going to test whether our car is able to accelerate and brake, so let's select those checkboxes:

Create test dialog

A new Python test class is created:

Test class

You can run the test by clicking the Run icon icon in the gutter next to the class definition. A Run/Debug configuration will be created automatically:

Running a test from the context menu
However, we can see that the test fails by default:

Failed test

Now we know that we can run tests, let’s start writing some actual test code.

Writing tests

How to write unit tests is out of scope for this article. If you’re interested in learning about using the `unittest` framework, you can check out their docs.

For our example let’s use these tests:

from unittest import TestCase from car import Car class TestCar(TestCase): def setUp(self): self.car = Car() class TestInit(TestCar): def test_initial_speed(self): self.assertEqual(self.car.speed, 0) def test_initial_odometer(self): self.assertEqual(self.car.odometer, 0) def test_initial_time(self): self.assertEqual(self.car.time, 0) def test_initial_average_speed(self): self.assertEqual(self.car.average_speed(), 0) class TestAccelerate(TestCar): def test_accelerate_from_zero(self): self.car.accelerate() self.assertEqual(self.car.speed, 5) def test_multiple_accelerates(self): for _ in range(3): self.car.accelerate() self.assertEqual(self.car.speed, 15) class TestBrake(TestCar): def test_brake_once(self): self.car.accelerate() self.car.brake() self.assertEqual(self.car.speed, 0) def test_multiple_brakes(self): for _ in range(5): self.car.accelerate() for _ in range(3): self.car.brake() self.assertEqual(self.car.speed, 10) def test_should_not_allow_negative_speed(self): self.car.brake() self.assertEqual(self.car.speed, 0) def test_multiple_brakes_at_zero(self): for _ in range(3): self.car.brake() self.assertEqual(self.car.speed, 0)

Running tests

Now let's run the tests. Right-click the test_сar.py editor tab and choose Run 'Python tests in test_car.py':

Running unittests in a file

This time almost all tests have passed successfully:

Run unittest

Debugging tests

Let's look closer at the test code and debug the test that has failed. Select the failed test in the left pane of the Run tool window, then find the line number where the error has occurred in the right pane:

Selecting the failing test in the Run tool window

Click the line number in the editor to set the breakpoint:

Test breakpoint

Next, launch a debugger session. To do that, right-click the editor background at the method test_initial_average_speed and choose Debug from the context menu:

Debug

The Debug tool window opens:

Debugging output

Click the Step into button to go into the class Car, and then expand the self node in the Debug tool window.

The debugger has highlighted the line that will be executed next (return self.odometer / self.time), and in the Debug tool window we can see that self.speed equals 0:

State before exception

This will lead to an exception on the next step, because division by zero is impossible. Let's fix that by adding some code to the class Car.

  1. Select self.odometer / self.time in the editor.

  2. Choose Refactor | Introduce Variable from the main menu or from the context menu, or press Control+Alt+V.

  3. Type the name of the variable, for example avg_speed:

    Introducing a variable
  4. Select the statement avg_speed = self.odometer / self.time, press Control+Alt+T (Code | Surround with), and then select if to add a condition to the statement.

    PyCharm creates a stub if construct:

    Adding a stub if construct
  5. We shouldn't divide by self.time if it equals 0. So, type self.time != 0 to specify the condition.

    Then specify that avg_speed should equal zero when self.time is zero. Here's what you should get:

    def average_speed(self): if self.time != 0: avg_speed = self.odometer / self.time else: avg_speed = 0 return avg_speed

Switch to test_car.py and run the tests again:

Passing tests

All tests have passed.

Running tests automatically

In the last paragraph, we reran our tests manually after fixing the Car class. If you'd like to focus on your code, and just see when you've resolved the issue, PyCharm can run the tests for you automatically.

Click More on the toolbar and select Rerun Automatically:

Turning on Auto-Test

Click the Run test automatically button on the Run toolbar.

Now, every time you enter changes in your project files (as it was done earlier), the tests will run without your intervention.

Last modified: 11 January 2024