0% found this document useful (0 votes)
133 views49 pages

Pytest: Effective Python Testing Guide

The document discusses using pytest to test Python code. It explains why testing is important, what pytest is, how to get started with pytest, running test cases, project structure, fixtures, and parameterizing tests. Some key points include: - pytest is a Python testing framework that makes testing easy, allows running specific tests, and can run tests in parallel. - To get started, install pytest, write some simple test functions, and run them with pytest. - Projects should have a structure with code in one folder and tests in another. - Fixtures in pytest are used for setup and teardown of tests. - Tests can be parameterized to run with different input values using the @pytest.mark.parametrize

Uploaded by

Sandeep Aggarwal
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
133 views49 pages

Pytest: Effective Python Testing Guide

The document discusses using pytest to test Python code. It explains why testing is important, what pytest is, how to get started with pytest, running test cases, project structure, fixtures, and parameterizing tests. Some key points include: - pytest is a Python testing framework that makes testing easy, allows running specific tests, and can run tests in parallel. - To get started, install pytest, write some simple test functions, and run them with pytest. - Projects should have a structure with code in one folder and tests in another. - Fixtures in pytest are used for setup and teardown of tests. - Tests can be parameterized to run with different input values using the @pytest.mark.parametrize

Uploaded by

Sandeep Aggarwal
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

PyCon Korea 2019

pytest로 파이썬 코드 테스트하기

최영선
[Link]@[Link]
PyCon Korea 2019
pytest
Why we should test code?
• Modify Code with Confidence

• Identify Bugs Early

• Improve System Design

[Link]
testing
What is pytest?
• python test framework.

• Advantages of pytest

• Easy to use

• Can run a specific test or subset of tests

• Skip tests

• Can run tests in parallel


What is pytest?
• Examples

• [Link]
PyCon Korea 2019
1. Getting Started
Getting Started
• Requirements

• Python 3

• virtualenv

• Installation
$ virtualenv –p python3.7 venv
$ source virtualenv/bin/activate
$ (venv)
• An example of simple test
# content of test_sample.py
def inc(x):
return x + 1

def test_answer():
assert int(3) == 5

• Execution
$ pytest test_sample.py
How to run test cases?
$ pytest -h
usage: pytest [options] [file_or_dir] [file_or_dir] [...]

positional arguments:
file_or_dir

• (venv) [Link] test_sample.py

• (venv) [Link] tests/test_sample.py

• (venv) [Link] tests/


Practice
# [Link]

def add(x, y):


return x + y

def subtract(x, y):


return x - y

def multiply(x, y):


return x * y

def divide(x, y):


return x / y
# [Link] # test_calculator.py

def add(x, y): import pytest


return x + y from calculator import add, divide

def subtract(x, y):


def test_add():
return x - y assert add(1, 2) == 3
assert not add (2, 2) == 3

def multiply(x, y): def test_divide():


return x * y with [Link](ZeroDivisionError):
divide(1, 0)

def divide(x, y):


return x / y
PyCon Korea 2019
2. Project Structure
Structure of the Repository
project/
sample/
__init__.py
[Link]
tests/
test_sample.py

• Run test

$ cd project
$ python –m pytest tests/test_sample.py
Practice
$ cd pytest_tutorial $ cd pytest_tutorial
$ ls $ mkdir src
[Link] $ mkdir tests
test_calculator.py $ mv [Link] src
$ mv test_calculator.py tests

$ pytest
# edit code
$ python –m pytest tests
PyCon Korea 2019
3. Fixture
Test Fixture
Four phases of a test:
1. Set-up
2. Exercise, interacting with the system under test
3. Verify, determining whether the expected outcome has been
obtained
4. Tear down, to return to the original state
• 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.

• Frequently fixtures are created by handling setUp() and tearDown() events of


the unit testing framework.
pytest fixtures

• Fixtures are run pytest before the actual test functions.

• @[Link]() decorator

• Purpose

• Setup and Teardown for the tests (e.g, database)

• Test set for the tests


Practice
# [Link]
class Calculator(object):
"""Calculator class"""
def __init__(self):
pass

@staticmethod
def add(a, b):
return a + b

@staticmethod
def subtract(a, b):
return a - b

@staticmethod
def multiply(a, b):
return a * b

@staticmethod
def divide(a, b):
return a / b
# [Link] # test_calculator.py
class Calculator(object): from [Link] import add
"""Calculator class"""
def __init__(self): def test_add():
pass assert add(1, 2) == 3

@staticmethod
def add(a, b):
return a + b

@staticmethod
def subtract(a, b):
return a - b

@staticmethod
def multiply(a, b):
return a * b

@staticmethod
def divide(a, b):
return a / b
# [Link] # test_calculator.py
class Calculator(object): from [Link] import Calculator
"""Calculator class""" def test_add():
def __init__(self): calculator = Calculator()
pass assert [Link](1, 2) == 3
assert [Link](2, 2) == 4
@staticmethod
def add(a, b): def test_subtract():
return a + b calculator = Calculator()
assert [Link](5, 1) == 4
@staticmethod assert [Link](3, 2) == 1
def subtract(a, b):
return a - b def test_multiply():
calculator = Calculator()
@staticmethod assert [Link](2, 2) == 4
def multiply(a, b): assert [Link](5, 6) == 30
return a * b
def test_divide():
@staticmethod calculator = Calculator()
def divide(a, b): assert [Link](8, 2) == 4
return a / b assert [Link](9, 3) == 3
# test_calculator.py
from [Link] import Calculator
def test_add():
calculator = Calculator()
assert [Link](1, 2) == 3
assert [Link](2, 2) == 4

def test_subtract():
calculator = Calculator()
assert [Link](5, 1) == 4
assert [Link](3, 2) == 1

def test_multiply():
calculator = Calculator()
assert [Link](2, 2) == 4
assert [Link](5, 6) == 30

def test_divide():
calculator = Calculator()
assert [Link](8, 2) == 4
assert [Link](9, 3) == 3
# test_calculator.py

from [Link] import Calculator

@[Link]
def calculator():
calculator = Calculator()
return calculator

def test_add(calculator):
assert [Link](1, 2) == 3
assert [Link](2, 2) == 4

def test_subtract(calculator):
assert [Link](5, 1) == 4
assert [Link](3, 2) == 1

def test_multiply(calculator):
assert [Link](2, 2) == 4
assert [Link](5, 6) == 30
# [Link] # test_calculator.py

import pytest from [Link] import Calculator

from [Link] import @[Link]


Calculator def calculator():
calculator = Calculator()
return calculator
@[Link]
def calculator(): def test_add(calculator):
calculator = Calculator() assert [Link](1, 2) == 3
return calculator assert [Link](2, 2) == 4

def test_add_fail(calculator):
assert [Link](1, 2) != 6
assert [Link](2, 2) != 5

def test_multiply(calculator):
assert [Link](2, 2) == 4
assert [Link](5, 6) == 30
# test_calculator.py

from [Link] import Calculator

def test_add(calculator):
assert [Link](1, 2) == 3
assert [Link](2, 2) == 4

def test_add_fail(calculator):
assert [Link](1, 2) != 6
assert [Link](2, 2) != 5
PyCon Korea 2019
4. Parameterize
pytest parameterize
• Define multiple sets of arguments and fixtures at the test function or class.

• @[Link]: parametrizing test functions

• The builtin [Link] decorator


Practice
# test_calculator.py

from [Link] import Calculator

def test_add(calculator):
assert [Link](1, 2) == 3
assert [Link](2, 2) == 4
assert [Link](2, 2) == 4

[Link](argnames, argvalues)
# test_calculator.py

from [Link] import Calculator

def test_add(calculator):
assert [Link](1, 2) == 3
assert [Link](2, 2) == 4
assert [Link](9, 2) == 11

@[Link](
"a, b, expected",
[(1, 2, 3),
(2, 2, 4),
(2, 7, 11)])
def test_add_fail(calculator, a, b, expected):
assert [Link](a, b) != expected
# test_calculator.py

from [Link] import Calculator

@[Link](
"a, b, expected",
[(1, 2, 6),
(2, 2, 5),
(2, 7, 2)])
def test_add_fail(calculator, a, b, expected):
assert [Link](a, b) != expected

@[Link](rason="wrong result")
@[Link](
"a, b, expected",
[(1, 2, 6),
(2, 2, 5),
(2, 7, 2)])
def test_add_fail(calculator, a, b, expected):
assert [Link](a, b) == expected
• Buildin markers: skip, skipif and fail

• skip: enable you to skip tests you don’t want to run

• @[Link](reason=‘something’)

• @[Link](condition, reason=‘something’)

• xfail: we are telling pytest to run a test function, but we expect it to fail.

• @[Link]
# test_calculator.py

from [Link] import Calculator

@[Link](rason="wrong result")
@[Link](
"a, b, expected",
[(1, 2, 6),
(2, 2, 5),
(2, 7, 2)])
def test_add_fail(calculator, a, b, expected):
assert [Link](a, b) == expected

@[Link](
"a, b, expected",
[[Link](1, 2, 6, marks=[Link]),
[Link](2, 2, 5, marks=[Link]),
[Link](2, 7, 2, marks=[Link])])
def test_add_fail(calculator, a, b, expected):
assert [Link](a, b) == expected
# test_calculator.py

from [Link] import Calculator

@[Link](
"a, b, expected",
[(1, 2, 3),
(2, 2, 4),
(2, 7, 9),
[Link](1, 2, 6, marks=[Link]),
[Link](2, 2, 5, marks=[Link]),
[Link](2, 7, 2, marks=[Link])])
def test_add(calculator, a, b, expected):
assert [Link](a, b) == expected
# test_calculator.py

from [Link] import Calculator

add_test_data = [
(1, 2, 3),
(2, 2, 4),
(2, 7, 9),
[Link](1, 2, 6, marks=[Link]),
[Link](2, 2, 5, marks=[Link]),
[Link](2, 7, 2, marks=[Link])
]

@[Link](
"a, b, expected", add_test_data)
def test_add(calculator, a, b, expected):
assert [Link](a, b) == expected
• ids: optional parameter to parameterize()

• @[Link]() decorator

• [Link](<value>, id=“something”)
# test_calculator.py

from [Link] import Calculator


add_test_data = [
(1, 2, 3),
(2, 2, 4),
(2, 7, 9),
[Link](1, 2, 6, marks=[Link]),
[Link](2, 2, 5, marks=[Link]),
[Link](2, 7, 2, marks=[Link])]

@[Link](
"a, b, expected", add_test_data, ids=[
"1 add 2 is 3",
"2 add 2 is 4",
"2 add 7 is 9",
"1 add 2 is not 6",
"2 add 2 is not 5",
"2 add 7 is not 2"] )
def test_add(calculator, a, b, expected):
assert [Link](a, b) == expected
# test_calculator.py

from [Link] import Calculator


add_test_data = [
(1, 2, 3),
(2, 2, 4),
(2, 7, 9),
[Link](1, 2, 6, marks=[Link]),
[Link](2, 2, 5, marks=[Link]),
[Link](2, 7, 2, marks=[Link])]

@[Link](
"a, b, expected", add_test_data, ids=[
"1 add 2 is 3",
"2 add 2 is 4",
"2 add 7 is 9",
"1 add 2 is not 6",
"2 add 2 is not 5",
"2 add 7 is not 2"] )
def test_add(calculator, a, b, expected):
assert [Link](a, b) == expected
# test_calculator.py

from [Link] import Calculator

subtract_test_data = [
[Link](5, 1, 4, id="5 subtract 1 is 4"),
[Link](3, 2, 1, id="3 subtract 2 is 1"),
[Link](10, 2, 8, id="10 subtract 2 is 8"),
[Link](5, 1, 6, marks=[Link], id="5 subtract 1 is 6"),
[Link](3, 2, 2, marks=[Link], id="3 subtract 2 is 2"),
[Link](10, 2, 1, marks=[Link], id="10 subtract 2 is 1")
]

@[Link](
"a, b, expected", subtract_test_data
)
def test_subtract(calculator, a, b, expected):
assert [Link](a, b) == expected
PyCon Korea 2019
5. Mock
pytest mock
• Thirty-party pytest plugin

• pytest-mock

• Swapping out part of the system to isolate bits of code

• Mock objects: test doubles, spies, fakes, or stubs..

• [Link]

• [Link]
Practice
• Installation
$ (venv) pip install pytest-mock
def test_add_with_mocker1(mocker, calculator):
"""Test functionality of add."""
[Link](calculator, 'add', return_value=5)
assert [Link](1, 2) is 5
assert [Link](2, 2) is 5

def test_add_with_mocker2(mocker, calculator):


"""Test functionality of add."""
[Link](calculator, 'add', side_effect=[1, 2])
assert [Link](1, 2) is 1
assert [Link](2, 2) is 2

def test_add_with_mocker3(mocker, calculator):


"""Test functionality of add."""
[Link](calculator, 'add', side_effect=ZeroDivisionError())
with [Link](ZeroDivisionError):
[Link](1, 2)
# [Link]
import logging

[Link](level=[Link])

class Calculator():
"""Calculator class"""

def __init__(self):
[Link] = [Link](self.__class__.__name__)

def add(self, a, b):


[Link](
"add {a} to {b} is {result}".format(
a=a, b=b, result=a + b
))
return a + b
@[Link](
"a, b, expected", add_test_data
)
# mocker: The mocker fixture is provided by the pytest-mock plugin
def test_add_spy_logger(mocker, calculator, a, b, expected):
spy_info = [Link]([Link], "info")
assert [Link](a, b) == expected
assert spy_info.called
assert spy_info.call_count == 1

calls = [[Link]("add {a} to {b} is {expected}".format(


a=a, b=b, expected=expected
))]
assert spy_info.call_args_list == calls
References
• [Link]

• [Link]

• [Link]
test-directory-structure

• [Link]

• [Link]

You might also like