changed 6 years ago
Published Linked with GitHub

Pytest 101

Speaker: Zong-han, Xie
https://hackmd.io/PdkOGp6NQDWJVLCxz06vWg


Disclaimer


Spaker only has written almost no test in his projects.


Installation of pytest

conda install pytest

or

pip install pytest

First test example


  1. create a function with the test_ prefix
def inc(x):
    return x + 1


def test_answer():
    assert inc(3) == 4

  1. Run the test
pytest
or
pytest -q test_simple.py 


File names and function names

  • By default pytest only identifies the file names starting with test_ or ending with _test as the test files.
  • Pytest requires the test method names to start with "test" .


Test Error Message

def inc(x):
    return x + 1


def test_answer():
    assert inc(3) == 5  # Error


Parameterized test

import pytest 

def add(x, y):
    return x + y

@pytest.mark.parametrize("x, y, z", [
    (1, 2, 3),
    (4, 5, 9),
    (3, 8, 12)  # pop error
])
def test_answer(x, y, z):
    print(x, y, z)
    assert add(x, y) == z


Skip

import pytest 

def add(x, y):
    return x + y

def test_func():
    assert 4 == 5

@pytest.mark.skip
@pytest.mark.parametrize("x, y, z", [
    (3, 8, 12)  # pop error
])
def test_add(x, y, z):
    assert add(x, y) == z


Conditional skip

import pytest 
import sys

def add(x, y):
    return x + y

@pytest.mark.skipif(sys.platform != 'win32', reason='Only run on win32')
def test_func():
    assert 4 == 5

@pytest.mark.parametrize("x, y, z", [
    (3, 8, 12)  # pop error
])
def test_add(x, y, z):
    assert add(x, y) == z



xfail (expected fail)

import pytest 

def add(x, y):
    return x + y

@pytest.mark.xfail
def test_func():
    assert 4 == 5


Fixture

  • A software test fixture sets up the system for the testing process by providing it with all the necessary code to initialize it, thereby satisfying whatever preconditions there may be. (wikipedia)
  • The purpose of test fixtures is to provide a fixed baseline upon which tests can reliably and repeatedly execute. (pytest doc)
  • Dependency injection

Simple fixture example

import pytest

class DBConnection(object):
    def query(self, stmt):
        return [('a', 'b', 1), ('d', 'ff', 9)]

@pytest.fixture
def db():    
    return DBConnection()

def test_query(db):
    data = db.query('some stmt')
    # use here to check data property
    assert len(data[0]) == 4 


conftest.py

# conftest.py
import pytest

class DBConnection(object):
    def query(self, stmt):
        return [('a', 'b', 1), ('d', 'ff', 9)]

@pytest.fixture
def db():
    return DBConnection()

# test_answer.py
def test_query(db):
    data = db.query('some stmt')
    # use here to check data property
    assert len(data[0]) == 4 



conftest.py: sharing fixture functions

  • Put fixtures into conftest.py, they will automatically included.
  • conftest.py can include some settings.
  • Every python package (a folder with init.py) can have their own conftest.py

conftest.py in main/sub directory

# __init__.py
# conftest.py
import pytest

class DBConnection(object):
    def query(self, stmt):
        return [('a', 'b', 1), ('d', 'ff', 9)]

@pytest.fixture(autouse=True)
def db():
    return DBConnection()

# test_answer.py
def test_query(db):
    data = db.query('some stmt')
    assert len(data[0]) == 4  # Pop error

conftest.py in main/sub directory

# subdir/__init__.py
# subdir/conftest.py
import pytest

class DBConnection(object):
    def query(self, stmt):
        return [('a', 'b', 1, 9), ('d', 'ff', 9, 11)]

@pytest.fixture(autouse=True)
def db():
    return DBConnection()

# subdir/test_answer.py
def test_query(db):
    data = db.query('some stmt')
    assert len(data[0]) == 3  # Pop error

2 errors!!


Scope of fixture

Scope Desc
function Run once per test
class Run once per class of tests
module Run once per module
session Run once per session

Scope of fixture

import pytest


@pytest.fixture(scope="session")
def resource_session(request):
    print('resource_session INIT')       
    request.addfinalizer(
        lambda: print('\nIn resource_session\'s end'))

@pytest.fixture(scope="module")
def resource_module(request):
    print('resource_module INIT')
    request.addfinalizer(
        lambda: print('\nIn resource_module\'s end'))

@pytest.fixture(scope="function")
def resource_func(request):
    print('resource_func INIT')
    request.addfinalizer(
        lambda: print('\nIn resource_func\'s end'))

Scope of fixture

# test_1_2.py
def test_one(resource_func):
    print('In test_one()')

def test_two(resource_module):
    print('\nIn test_two()')
# test_3_4.py
def test_three(
    resource_func,
    resource_session, 
    resource_module):
    print('\nIn test_three()')


def test_four(resource_func, resource_module):
    print('\nIn test_four()')





fixture X fixture

# conftest.py
import pytest

@pytest.fixture(scope='module', params=['a', 'b'])
def resource_1(request):
    param = request.param
    return 'resource_1_' + param


@pytest.fixture(scope='module', params=['a', 'b'])
def resource_2(request):
    param = request.param
    return 'resource_2_' + param


fixture X fixture

# test_answer.py
def test_1(resource_1):
    print('In test_1: ' + resource_1)

def test_2(resource_2):
    print('In test_2: ' + resource_2)

def test_3(resource_1, resource_2):
    print('In test_3: ' + resource_1 + ', ' + resource_2)

fixture X fixture


configuration with conftest.py

# conftest.py
import pytest
import sys

def pytest_addoption(parser):
    parser.addoption(
        "--cmdopt", action="store", 
        default="type1", 
        help="my option: type1 or type2")

def pytest_configure(config):
    sys._called_from_test = 'True'

def pytest_unconfigure(config):
    del sys._called_from_test

def pytest_report_header(config):
    return "This is header"

@pytest.fixture
def cmdopt(request):
    return request.config.getoption("--cmdopt")


configuration with conftest.py

# test_answer.py
import argparse
import sys

def test_answer(cmdopt):
    print("sys._called_from_test: ", sys._called_from_test)
    if cmdopt == 'type1':
        print('first')
    elif cmdopt == 'type2':
        print('second')
    assert True



More detail please go to


Thank you!

Q&A


Select a repo