Hands-on! Start Testing with PyTest
In the previous article, we talked about why I have chosen Pytest as the test framework and have yet to mention what you are capable of doing with it. In this article, I want to show you the Pytest from the start. I want it to be a kind of tutorial for the Pytest. There is so much about Pytest and its features to talk about.
Start Using Pytest
It is quite simple to start using it. You only need to type the following command line to install the latest version of Pytest.
> pip install pytest
Now you have Pytest installed and you can start using it.
Let’s start with some basic rules and suggestions for using Pytest.
- Pytest is able to identify the test files and methods with valid naming. When you create your test file name it with the “test” keyword with an underscore(“_”) such as test_example.py, example_test.py.
If you are naming the test file in another way, Pytest will not recognize the file and if you want to run the file you will need to mention the file directory and name explicitly.
- Your Pytest methods should also include the “test” keyword. Here you can find some valid naming examples: def test_example1_method1():, def testexample1_method1():
If you name the method in some other way like example_method1(), Pytest will not run this method.
- Give meaningful and explanatory messages to your assertions
assert a ==b, “Test failed: x=”+ str(x) + “ not equal to” + “ y=” + str(y)
- Use marks to specify your test cases. In this way, you can run specific test cases with specific marker names. @pytest.mark.<name>
- Use Pytest fixture methods to make your test automation more advanced. Create a file “conftest.py” to use fixtures in multiple test files.
- Parameterize your tests if you are running a test with multiple sets of arguments. You can do it with a mark @pytest.mark.parametrize
Pytest with Examples
I mentioned a few suggestions above and I will detail them with examples in this section. Let’s start with a basic example.
Create a file named example1_test.py in the folder.
Always start with importing pytest for each test file and start defining your test methods. Here below you can find a sample for a basic test file.
import pytest
def test_method1():
a = 5
b = 6
assert a == b, "test failed"
def test_method2():
a = 5
b = 6
assert b - 1 == a, "test failed"
You can run the test using the following command
> py.test
You will have an output as:
Pytest output has an “F.”. In the output, F symbolizes a failed test, and dot(.) symbolizes a successful test.
The “py.test” command runs all the test files, but you can also run a specific test file by using the file name.
> py.test example1_test.py
If you don’t want to run an entire test suite you can run a specific test or test group. Grouping test names with substring matching or grouping with markers can help you with that.
1- You can run tests by substring matching
Let’s say we have the following test files and methods
test_file1( test_file1_method1, test_file1_method2)
test_file2( test_file2_method1, test_file2_method2)
> py.test -k method1 -v
This command will run tests with substring “method1” and test_file1_method1 and test_file2_method1 will be executed.
2- You can run tests by using markers
Pytest allows us to set attributes for test methods using markers as I mentioned in the previous section by using “@pytest.mark.<name>”
We can set markers for each method in a test file and you can use them to distinguish while running tests.
import pytest
@pytest.mark.group1
def test_method1():
a = 5
b = 6
assert a == b, "test failed"
@pytest.mark.group2
def test_method2():
a = 5
b = 6
assert b - 1 == a, "test failed"
@pytest.mark.group1
def test_method3():
x = 1
y = 100
assert x * 100 == y
You have to register your custom marks in pytest.ini file. This file is a configuration file for Pytest. You should create it in the root directory.
pytest.ini file:
[pytest]
markers =
group1:Run the group1 tests
group2:Run the group2 tests
Use the following command to run a group of tests with a specific marker
> py.test -m group1
The result for the group1 mark should be like
Parametrize your tests
It is a good practice to parameterize your test cases when you have to run the same test cases with different arguments multiple times. In this way, you will not have code duplications and unnecessary effort. It is easier to manage and maintain if you parametrize the arguments.
import pytest
@pytest.mark.square
@pytest.mark.parametrize("xEdge, yEdge", [(30, 10), (20, 20)])
def test_squareArea(xEdge, yEdge):
x = xEdge
y = yEdge
area = x * y
assert area == x * x, "Failed: Not Square"
When we run the parametrized test, it will run the test two times for each argument set in the example. The result will be as the following
Pytest Fixtures
Pytest fixtures are so much useful when you want to run some repeated codes before every test method or use the repeated code for multiple tests. Using fixtures will advance your tests and as I mentioned before, you only need to mark the method with the “@pytest.fixture” tag.
You can have fixture methods in a test file. If you want to use the same fixture methods for multiple test files you can create a conftest.py file as I mentioned previously. It is a recognizable configuration file for the Pytest.
You can check an example of using the fixture method and other Pytest features used at the same time within the following example.
I created a balance.py class as a controller class that contains the balance object and methods for actions deposit and withdraw. When we call the depositToBalance method, it adds money to the balance value. The withdrawFromBalance method takes out the money from the balance value and getBalance returns the balance value.
balance.py:
class Balance(object):
def __init__(self, initial_amount=0):
self.balance = initial_amount
def getBalance(self):
return self.balance
def depositToBalance(self, amount):
self.balance += amount
return self.balance
def withdrawFromBalance(self, amount):
if self.balance < amount:
print("Insufficient Funds")
return False
else:
self.balance -= amount
return self.balance
And I have a test class for balance actions. We have a playerBalance method as a fixture so I will be using this method for multiple test methods. We have a test for the initial balance which is defined as zero as default. And we have tests for deposit and withdraw actions that are defined in the balance.py and one method for insufficient balance test. I parametrized those tests so we will be able to test for various amounts. This way is very efficient for boundary tests.
test_balance.py:
import pytest
from balance import Balance
@pytest.fixture
def playerBalance():
return Balance(0)
@pytest.mark.balance
def test_initial_balance(playerBalance):
assert playerBalance.balance == 0, "Initial balance should be zero."
@pytest.mark.balance
@pytest.mark.parametrize("depositAmount", [10, 20, 30])
def test_depositAction(playerBalance, depositAmount):
balance = 0
balance += depositAmount
playerBalance.depositToBalance(depositAmount)
assert balance == playerBalance.getBalance()
@pytest.mark.balance
@pytest.mark.parametrize("withdrawAmount", [10, 20, 30])
def test_InsufficientFunds(playerBalance, withdrawAmount):
balance = playerBalance.balance
assert playerBalance.withdrawFromBalance(withdrawAmount) == False
@pytest.mark.balance
@pytest.mark.parametrize("withdrawAmount", [10, 20, 1000])
def test_withdrawAction(playerBalance, withdrawAmount):
playerBalance.depositToBalance(100)
balance = 100
if balance < withdrawAmount:
balance = 100
else:
balance -= withdrawAmount
assert playerBalance.withdrawFromBalance(withdrawAmount) == balance
I have an intended failed test for the withdraw action with an insufficient amount in the above test file. When we run the test file with the mark balance, which I added my custom mark definition in the pytest.ini file, the test result should be like
At the end of this article, we made a brief for excellent Pytest features that are very useful while writing your test automation. I tried to show you some useful tips and examples and I hope they will be helpful to you. In order to have an advanced test automation project don’t neglect to use Pytest features like fixtures, marks, and parameterize. They are for saving your life. Happy testing!