Pytest is a open source framework to perform test automation for python. Most excited part for me is that , so easy to start with and can be used for all types and levels of software testing.
Libraries are just like treats given by pytest. Some of the treats are like fixtures, parameterization, skipping tests and so on.
Naming conventions of pytest -
Pytest file should start with either test_filename or filename_test
Naming convention for all functions should start with test_
Topics covered in this blog,
Creating a pytest file.
Basic pytest commands
Assert statement
Why naming conventions for functions are mandatory
Substring matching
Fixtures
Lets see some of the commands to get started with pytest,
Pytest installation -
With python installation comes pip- a package management system used to install and manage software packages written in Python.
pip install pytest
Pytest version check ,
To check latest version of pytest, execute the following command prompt,
pytest --version
Help information,
To display help information such as reportings, pytest warnings,test seesion debugging and configuration etc... execute the following command prompt,
pytest -h
Creating a pytest file -
Now , lets start with our first pytest program.
First we have to create a directory and then, create a test file in the directory.
Here, Test is a package created under a python project then create a new python file.
Now lets see the operations in first test file - test_stringOps.py
Write following code to file, in which basic string operations are performed...
# Replacing String with another string
def test_strReplace():
string = "Hello, World!"
assert string.replace("H", "J") == "Jello, World!"
# String Split - Splits a string to two substrings
def test_strSplit():
string = "Hello,World"
assert string.split(",") == ["Hello", "World"]
# String Strip
def test_strStrip():
string = " Hello, World! "
assert string.strip() == "Hello, World!"
# String Concatenate
def test_strConcat():
string1 = "Hello"
string2 = "World"
assert string1 + string2 == "HelloWorld"
By seeing code, we can understand that everything should be in a function and each function is having an assert statement. Lets see what this assert statement does...
Assert statement -
In each and every function, last statement seen is assert statement.
Depending upon the execution of test function, a value is returned. This return value can be either True or False.
In pytest, if an assertion fails in a test function, then that function execution is stopped and next statements in that test functionare not executed, and continue to next function.
Now lets execute the above string operations file, this can be done in different ways.
Command that can be used to trigger all the files in the current directory and subdirectories is,
pytest
To execute a particular test file , syntax is
pytest filename.py
example -to execute tests in test_stringOps.py file, command should be
pytest test_stringOps.py
If we would like to execute only a particular function, then mention function name as,
pytest filename.py::function_name
example - ro execute test_strConcat functionin test_stringOps.py file,
pytest test_stringOps.py::test_strConcat
Why naming convention to a function is mandatory
Here is the example in which we are going to see how naming convention for function works.
File name is test_arithmetic.py, in which four functions are with test_naming convention. Where as third function doesn't follow the naming convention. If we try to execute this file, only three functions data was collected and the other function multiply() was not passed.
def test_subtract():
a = 6
b = 6
assert a - b == 0
def test_divide():
a = 6
b = 6
assert a / b == 1
def multiply():
a = 6
b = 6
assert a - b == 0
def test_multiply():
a = 6
b = 6
assert a * b == 36
Output,
collected 3 items
test_arithmetic.py ... [100%]
==================================== 3 passed in 0.06s =======================================================================
Substring matching
Suppose, if we want to run only a specific set of tests, this can be done by marking the tests and run tests based on substring matching.
To execute the tests containing a string in its name we can use the following syntax −
pytest -k <substring> -v
for following tests, lets execute with substing "str"
# Replacing String with another string
def test_strReplace():
string = "Hello, World!"
assert string.replace("H", "J") == "Jello, World!"
# String Split - Splits a string to two substrings
def test_strSplit():
string = "Hello,World"
assert string.split(",") == ["Hello", "World"]
# String Strip
def test_strStrip():
string = " Hello, World! "
assert string.strip() == "Hello, World!"
# String Concatenate
def test_strConcat():
string1 = "Hello"
string2 = "World"
assert string1 + string2 == "HelloWorld"
to execute tests based on matching substring, execute with following command,
pytest -k str -v
This will execute all the test names having the word ‘str’ in its name. In this case, they are test_strReplace() ,test_strSplit() , test_strStrip() and test_strConcat().
test execution process,
test_stringOps.py::test_strReplace PASSED [ 25%]
test_stringOps.py::test_strSplit PASSED [ 50%]
test_stringOps.py::test_strStrip PASSED [ 75%]
test_stringOps.py::test_strConcat PASSED [100%]
============================= 4 passed, 10 deselected in 0.07s =======================================================================
Here comes another important concept of pytest. In the below code str variable is declared twice and what if we have more functions which uses this variable.
# Uppercase
def test_upper():
str ="python"
assert str.upper() == "PYTHON"
# Is Alpha
def test_isalpha():
str = "python"
assert str.isalpha() == True
In order to decrease the length of code, we can keep this input data in a fixture and use it when ever we need. This can be done using Fixtures.
What are Fixtures
Fixtures are functions which give data to other functions. This concept is simple yet most powerful in pytest framework.
These fixtures run before and then execution of test function follows. A function can declared as a fixture by,
@pytest.fixture
Lets, create a fixture for above string operations file and start using the string variable from calling fixture.
import pytest
@pytest.fixture
def input_value():
string = "python"
return string
So, fixture function name is passed as arguments for corresponding test functions and used where ever needed. Here, in fixture function, string variable is returned. This is accesses by rest of the test functions , instead of repeating the same code again and again.
# Uppercase
def test_upper(input_value):
assert input_value.upper() == "PYTHON"
# Is Alpha
def test_isalpha(input_value):
assert input_value.isalpha() == True
The major advantage of using fixtures reduces the code complexity, length of code and cost as well. For example, while establishing database connection, we can make use of these fixtures to code data regarding setting up connection in one place and make use of it where ever needed.
This type of approach has again a drawback. If we want to use a fixture we can only use it with in a file as its scope is within the file.
Here comes another concept named as conftest.py.
We define a fixture function in a file named conftest.py in order to share the code to multiple test files.
Now lets take above example and see how it works. Here we have two test files named as
test_strBasicOps.py and test_strPalindrome.py. In both files we are doing some string operations to perform and for these input data is taken from a common fixture named conftest.py.
In conftest.py,
import pytest
@pytest.fixture
def input_value():
string = "python"
return string
In test_strBasicOps.py,
# Uppercase
def test_upper(input_value):
assert input_value.upper() == "PYTHON"
# Length of a string
def test_len(input_value):
assert len(input_value) == 6
In test_strPalindrome.py,
# Palindrome
def test_isPalindrome(input_value):
if input_value == input_value[::-1]:
assert True
else:
assert False
Fixture named, input_value is passed as an argument for the functions defined in both the files and executed tests.
Output from both files,
Executing file test_strBasicOps.py using command pytest test_strBasicOps.py
collected 2 items
test_strBasicOps.py .. [100%]
==================================== 2 passed in 0.04s =======================================================================
Executing file test_strPalindrome.py using command pytest test_strPalindrome.py
__________________________________
test_isPalindrome
______________________________________
input_value = 'python'
def test_isPalindrome(input_value):
if input_value == input_value[::-1]:
assert True
else:
> assert False
E
assert False
test_strPalindrome.py: 7: AssertionError
== == == == short
test
summary
info == == == == == == == == == == == == == == == == == == == == == ==
FAILED
test_strPalindrome.py::test_isPalindrome -
assert False
== == == == == = 1
failed in 0.14
s == == == == == == == == == == == == == == == == == == == == == == ==
here assertion failed as output doesn't match.
Let’s discuss about other libraries and functionalities of pytest in my next blog.
Thanks for reading…!!