• Lalitha Gunturu

Pytest - a beginner guide. 📄

Updated: Nov 3, 2020


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

  • conftest.py


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.

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…!!


38 views0 comments

Recent Posts

See All
 

© Numpy Ninja.