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.
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 CtrlAlt0S to open settings and select Tools | Python Integrated Tools, and then select the target test runner from the Default test runner list.
For more information, refer to Testing frameworks.
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 CtrlShift0T):
A popup appears that suggests creating 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:
A new Python test class is created:
You can run the test by clicking the icon in the gutter next to the class definition. A Run/Debug configuration will be created automatically:
However, we can see that the test fails by default:
Now we know that we can run tests, let’s start writing some actual test code.
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)
Now let's run the tests. Right-click the test_сar.py editor tab and choose Run 'Python tests in test_car.py':
This time almost all tests have passed successfully:
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:
Click the line number in the editor to set the 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:
The Debug tool window opens:
Click the 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:
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
.
Select
self.odometer / self.time
in the editor.Choose Refactor | Introduce Variable from the main menu or from the context menu, or press CtrlAlt0V.
Type the name of the variable, for example
avg_speed
:Select the statement
avg_speed = self.odometer / self.time
, press CtrlAlt0T (Code | Surround with), and then select if to add a condition to the statement.PyCharm creates a stub
if
construct:We shouldn't divide by
self.time
if it equals 0. So, typeself.time != 0
to specify the condition.Then specify that
avg_speed
should equal zero whenself.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:
All tests have passed.
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 on the toolbar and select Rerun Automatically:
Click the 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.
Thanks for your feedback!