SlideShare uma empresa Scribd logo
1 de 57
Baixar para ler offline
Effective testing with Pytest
@hectorcanto_dev
slideshare.net/HectorCanto
github.com/hectorcanto/pytest-samples
Summary
We will focus in Python testing with Pytest, but
many things apply to any test framework in any
language
Why pytest
IMHO pytest is a top-grade framework, updated,
very well documented,
and compatible with many
tools.
Why we need testing
effectively
Testing takes a good part of our time working on
Software
The best we test, the better software we make
The most effective we are, more time to do other
stuff
Testing is very important
Guarantees all our code works when adding new
features
Documents it with examples and context
Debugging environment
Enables refactoring
Article: Tips and tricks for unit tests
Effectivity means
Running test as fast as possibly
Developing and maintaining code
Readable for everyone, especially newcomers
How we achieve effectivity
Using Pytest to its full potential
Understanding artifacts: mocks, fixtures …
Applying best practices
Adding good libs and plugins
Planning and common sense
Test phases
A test can be divided in 3 phases know as the triple
A (AAA)
Arrange
Act
Assert
We will have points on every one of them
Launching the suite
Pytest options
Markers
Test selection
Launch only what you need at every moment
Pytest options
We can filter tests in several ways
pytest --help

pytest tests/folder/test_file.py::test_name

pytest -m smoke

pytest -k users
Suite: repeat failed tests
We will give priority to failed ones
Suite: specific tests
Naming is key
pytest --fail-first

pytest --last-failed

pytest --failed-first
def test_users_creation():

...

def test_users_update():

...
pytest -k users
Suite: markers
Useful markers: smoke, unit, integration, current,
slow
@pytest.mark.slow

@pytest.mark.current

@pytest.mark.skip(reason="whatever")

@pytest.mark.xfail

def test_this():

...
pytest -m current -s -v

pytest -m "slow and not integration"

pytest -m "smoke and unit"
Suite: global markers
It is easy to mark groups of tests
# Per module

pytestmark = pytest.mark.auth

pytestmark = [pytest.mark.deletion, pytest.mark.api]

# Per class

class TestClass:

pytestmark = pytest.mark.special
Suite: Folder structure
tests
├── smoke/

├── unit/

├── integration/

├── api/

├

├── fixtures/

├── factories/

├── conftest.py

├── aux.py

└── __init__.py
Suite: automatic markers
Auto-mark tests by folder at tests/conftest.py
def pytest_collection_modifyitems(items):

for item in items:

if "/smoke/" in str(item.module):

item.add_marker("smoke")
Launching the suite: test types
Smoke tests: very simple ones. Run first
Unit test: small and independent. Run often
Integration: they need something external. Run
sometimes
API: outside the box. Run sometimes
Suite: ordering
Ordering is useful for smoke tests and on CI
pipeline
Randomization helps finding lateral effects
@pytest.mark.first

@pytest.mark.second

@pytest.mark.last
Plugins: pytest-ordering, pytest-randomly
Suite: environment
We keep local and test envs apart
# setup.cfg

[tool:pytest]

env =

PYTHONBREAKPOINT=ipdb.set_trace

APP_ENVIRONMENT=test

CACHE=memory

DEBUG=1

VAR=value
Plugin: pytest-env
Suite: environment 2
def test_with_different_env_vars(monkeypatch):

monkeypatch.setenv("CACHE", "nocache")

monkeypatch.delenv("VAR")
Fixture: *monkeypatch*
Setup or Arrange
Parametrization
Fixtures
Factories
Setup: Parametrization
Instead of making a test for each input
<ake one test for all inputs
@pytest.mark.parametrize("entry, expected", (

(1, True),

(2, False),

(None, False)

("hello", False)

)

def test_something(entry, expected: bool):

result = function_under_test(entry)

assert result == expected
Fixtures
In general, fixtures are the artifacts all around a
test
Synthetic data used as input
The system in a specific state
Active elements that interact with the SUT
Pytest fixtures
In Pytest, fixtures are created with an especial
decorator
There might be fixtures with setup, teardown or
both
@pytest.fixture

def example_fixture()

now = datetime.utcnow()

do_setup(now)

return now

do_teardown()
Fixture example
@pytest.fixture

def load_data(db_client):

my_user = User(name="Hector", last_name="Canto")

db_client.add(my_user)

yield my_user # usable as parameter

db_client.delete(my_user)
Fixture usage
Place your fixtures on any conftest.py
No need to import your fixtures
@pytest.mark.usefixtures("load_data")

def test_with_fixtures(example_fixture):

result, error = system_under_tests(example_fixture)

assert result

assert not error
Reuse fixtures
Scopes: session, module, class or function
@pytest.fixture(scope="session")

def test_settings():
yield get_settings(test=True)
Automatic fixtures
It always runs
It depends on another fixture
@pytest.fixture(autouse=True)

def clean_db(db_client):

yield db_client

for table in db_client.tables:

db_client.truncate()
Data fixtures
Import constants
Generation functions
Factories
No raw json, text files …
Create your data fixture programmatically
Setup: Factory
from factory import StubFactory

class ObjectFactory(StubFactory):

name: str = "value"



my_ojb = ObjectFactory()

assert my_obj.name == "value"

other = ObjectFactory(name="another value")

assert other.name == "another value"
Libary & plugin: factoryboy, pytest-factoryboy
Factory & Faker
Generate multiple values in one go
from factoryboy import Faker, SelfAttribute

from factory.fuzzy import FuzzyInteger

class ObjectFactory(StubFactory):

name: str = Faker("first_name_male")

last_name: str = Faker("last_name")

full_name: str = SelfAttribute(lambda self: f"{self.name} {se
phone: str = Faker("phone_number", local="en_GB")

money: int = FuzzyInteger(1, 1000)
Library: Faker
Model Factory
Can be used with Django, SQLAlchemy and
Pymongo
from factory import alchemy, RelatedFactory

class UserFactory(alchemy.SQLAlchemyModelFactory):

class Meta:

model = User

sqlalchemy_session = Session

id = LazyFunction(lambda: randint(1, 1_000_000))

location = RelatedFactory(LocationFactory)

created_at = Faker(

"unix_time", 

start_datetime=datetime(2015, 1, 1), 

end_datetime=datetime(2019, 12, 31)

)
Batches & Dict Factory
class UserDictFactory(DictFactory)
…
factory.build(dict, FACTORY_CLASS=UserFactory)

UserFactory.create_batch(5)

UserFactory.build_batch(5) # created but not persisted
Generate _inputs_ for an API or a function

<aside class="notes">

</aside>
Execution or Act
Control Time
Test doubles: Mocks and other
Interceptors
Freezegun
from datetime import datetime

from freezegun import freeze_time

@freeze_time("2012-01-14")

def test_2012():

assert datetime.now() == datetime(2012, 1, 14)
with freeze_time("2018-01-01"):

assert datetime.now() > datetime(2012, 1, 4)
Library: freezegun
Freezer
from datetime import datetime

def test_freezer_move(freezer):

now = datetime.now()

freezer.move_to('2017-05-20')

later = datetime.now()

assert now != later

@pytest.mark.freeze_time('2017-05-21')

def test_freeze_decorator():

assert datetime.now() == datetime(2017, 5, 21)
Fixture: freezer
Other date and time libraries
timeago - moment - pytime - arrow
timeago.format(timedelta(seconds = 60 * 3.4)) # 3 minutes ago

moment.date(2012, 12, 19).add(hours=1, minutes=2, seconds=3) # n
pytime.next_month('2015-10-1') # again, no-more-deltas

arrow.utcnow().span('hour') # 2 datetimes in one line
Execution: test doubles
Doubles replace some element to provide the
desired behaviour
Test doubles types
Mock library
In python most doubles are implemented by the
mock library
Library: mock - Plugin: pytest-mock - Fixture: mocker
Python mocks
def test_mock_patching(mocker):

url = "https://2021.es.pycon.org/" 

mocked = mocker.patch.object(requests, "get", return_value="i
mocker.patch.object(requests, "post", side_effect=ForbiddenEr
response = requests.get(url)

assert response == "intercepted"

assert mocked.called_once()
Stubs
def test_stubbing(monkeypatch):

def mock_exist(value):

print(f"{value} exists")

return True

monkeypatch.setattr(os.path, 'exists', mock_exist)

assert os.path.exists("/believe/me/I/exist")
Spies
It does not intercept, only registers callbacks
def test_with_spy(mocker):

url = "https://2021.es.pycon.org/"

spy = mocker.spy(requests, "get")

response = requests.get(url)

assert response.status_code -= 200

spy.assert_called_once(), spy.mock_calls

spy.assert_called_with(url)
Interceptors
Intercepts callbacks, returns what you need
def test_with_http_interceptor(requests_mock):

# arranges

url = "http://tests.com"

requests_mock.get(url, json={"key": "value"})

# action

response = requests.get(url)



assert "key" in response.json()
Libray: requests_mock
Validation or assert
def test_one():

expected = 5

result = system_under_test()

assert result

assert result is not None

assert result == expected

assert result > 3
Assertion error messages
In case of AssertionError
we expose some extra information
response = requests.get(url)

assert response.json() == expected, response.text()
Comparisons
Don’t lose time on unimportant differences
import pytest, math

assert 2.2 == pytest.approx(2.3, 0.1)

assert math.isclose(2.2, 2.20001, rel_tol=0.01)
Comparisons: list and sets
Don’t lose your head around repeated values
nor with loops comparing list
my_list = [1, 1, 2, 3, 4]

other_list = [4, 3, 2, 1]

list_without_duplicates = list(set(my_list))

diff = set(my_list) ^ set(other_list)

assert not diff
Comparison: dicts
Do not lose time ordering responses
Ignore painful fields like dates
from deepdiff import DeepDiff

def test_dicts(parameter, expected):

result = system_under_test(parameter)

diff = DeepDiff(result, expected, exclude_paths=(f"root['upda
assert not diff, diff
Library: deepdiff
Assert at the end
def test_delayed_response(requests_mock):

url = "http://tests.com"

requests_mock.get(url, json={"key": "value"})

response = requests.get(url)

expect(response.status_code == 200, response.status_code)

expect(response.json() == {}, response.text)

assert_expectations()
Library: delayed-assert
Delayed assert prompt
def assert_expectations():
'raise an assert if there are any failed expectations'

if _failed_expectations:

> assert False, _report_failures()

E AssertionError: 

E 

E assert_expectations() called at

E "/home/hector/Code/personal/effective_testing/tests/u
E 

E Failed Expectations : 1

E 

E 1: Failed at "/home/hector/Code/personal/effective_te
E ErrorMessage: {"key": "value"}

E expect(response.status_code == 200, response.stat
../../../.local/share/virtualenvs/effective testing-QL w0uj8/lib/
Log validation
Log are key for several reason
Monitoring and metrics
Treaceability and error control
Especially important on microservices and
serverless
Log capture
def test_log_capture(request, caplog):

logger = logging.getLogger(request.node.name)

caplog.set_level("INFO")

dtt = "2021-10-02 10:55:00"

msg = "captured message"

expected_message = f"{dtt} INFO module:{request.node.name} {m
with freeze_time(dtt):

logger.info("captured message")

with caplog.disabled():

logger.info ("Any log here will not be captured")

some_function_with_logs()

breakpoint()



with freeze_time(dtt):

logger.info("captured message")
Fixture: caplog, capsys, capfd
Extra ball: chasing errors
Tests are little environments you can reuse to
look for unexpected errors
Debugging in live servers is not always possible
nor easy
Library: ipdb, IDEs
pip install ipdb

PYTHONBREAKPOINT=ipdb.set_trace pytest -m current -s
Use your IDE built-in breakpoints and debugger
Anti-patterns
Too many mocks
Fighting the framework
Sequential tests
Dirty sources
Test Python or a library
https://www.softwaretestingmagazine.com/knowledg
patterns-in-software-testing/
https://dzone.com/articles/unit-testing-anti-
patterns-full-list
Final ideas
“Tests are a waste of time”
Actually they save you a lot of time
Treat tests as first-class citizens:
style, docstrings, comments …
There is no small project that is worth having
tests
Think on your peers and your future self
Remember the test pyramdg, don’t put all your
stakes in one type of tests
Recommendations
If something is taking too long to do
there’s a library or a recipe that can help you
Read and revisit the documentation
you will always find something new
Frameworks help you, check their test
documentation
Summary
Fixtures: monkeypatch, mocker, requests_mock,
caplog, parametrize, mark
Libraries: factoryboy,
faker, deepdict, freezegun, moment, ipdb
https://docs.pytest.org/
https://docs.pytest.org/en/latest/reference/plugin_lis
Thanks!
Thanks for your attention, hope you like it
Any question, suggestion … ?
https://github.com/hectorcanto/pytest-samples
https://www.slideshare.net/HectorCant

Mais conteúdo relacionado

Mais procurados

Python RESTful webservices with Python: Flask and Django solutions
Python RESTful webservices with Python: Flask and Django solutionsPython RESTful webservices with Python: Flask and Django solutions
Python RESTful webservices with Python: Flask and Django solutionsSolution4Future
 
Python Flask Tutorial For Beginners | Flask Web Development Tutorial | Python...
Python Flask Tutorial For Beginners | Flask Web Development Tutorial | Python...Python Flask Tutorial For Beginners | Flask Web Development Tutorial | Python...
Python Flask Tutorial For Beginners | Flask Web Development Tutorial | Python...Edureka!
 
Javascript this keyword
Javascript this keywordJavascript this keyword
Javascript this keywordPham Huy Tung
 
Python: Modules and Packages
Python: Modules and PackagesPython: Modules and Packages
Python: Modules and PackagesDamian T. Gordon
 
Learn flask in 90mins
Learn flask in 90minsLearn flask in 90mins
Learn flask in 90minsLarry Cai
 
Windows Operating System Archaeology
Windows Operating System ArchaeologyWindows Operating System Archaeology
Windows Operating System Archaeologyenigma0x3
 
Mocking in Java with Mockito
Mocking in Java with MockitoMocking in Java with Mockito
Mocking in Java with MockitoRichard Paul
 
Node.js Tutorial for Beginners | Node.js Web Application Tutorial | Node.js T...
Node.js Tutorial for Beginners | Node.js Web Application Tutorial | Node.js T...Node.js Tutorial for Beginners | Node.js Web Application Tutorial | Node.js T...
Node.js Tutorial for Beginners | Node.js Web Application Tutorial | Node.js T...Edureka!
 
An introduction to Google test framework
An introduction to Google test frameworkAn introduction to Google test framework
An introduction to Google test frameworkAbner Chih Yi Huang
 
Web develop in flask
Web develop in flaskWeb develop in flask
Web develop in flaskJim Yeh
 
Introduction to Gitlab | Gitlab 101 | Training Session
Introduction to Gitlab | Gitlab 101 | Training SessionIntroduction to Gitlab | Gitlab 101 | Training Session
Introduction to Gitlab | Gitlab 101 | Training SessionAnwarul Islam
 
Spring boot Introduction
Spring boot IntroductionSpring boot Introduction
Spring boot IntroductionJeevesh Pandey
 
Django Introduction & Tutorial
Django Introduction & TutorialDjango Introduction & Tutorial
Django Introduction & Tutorial之宇 趙
 
Laravel Design Patterns
Laravel Design PatternsLaravel Design Patterns
Laravel Design PatternsBobby Bouwmann
 
A Basic Django Introduction
A Basic Django IntroductionA Basic Django Introduction
A Basic Django IntroductionGanga Ram
 

Mais procurados (20)

NestJS
NestJSNestJS
NestJS
 
Python RESTful webservices with Python: Flask and Django solutions
Python RESTful webservices with Python: Flask and Django solutionsPython RESTful webservices with Python: Flask and Django solutions
Python RESTful webservices with Python: Flask and Django solutions
 
Python Flask Tutorial For Beginners | Flask Web Development Tutorial | Python...
Python Flask Tutorial For Beginners | Flask Web Development Tutorial | Python...Python Flask Tutorial For Beginners | Flask Web Development Tutorial | Python...
Python Flask Tutorial For Beginners | Flask Web Development Tutorial | Python...
 
Gradle Introduction
Gradle IntroductionGradle Introduction
Gradle Introduction
 
Git & GitHub for Beginners
Git & GitHub for BeginnersGit & GitHub for Beginners
Git & GitHub for Beginners
 
Javascript this keyword
Javascript this keywordJavascript this keyword
Javascript this keyword
 
Python: Modules and Packages
Python: Modules and PackagesPython: Modules and Packages
Python: Modules and Packages
 
Learn flask in 90mins
Learn flask in 90minsLearn flask in 90mins
Learn flask in 90mins
 
Windows Operating System Archaeology
Windows Operating System ArchaeologyWindows Operating System Archaeology
Windows Operating System Archaeology
 
Mocking in Java with Mockito
Mocking in Java with MockitoMocking in Java with Mockito
Mocking in Java with Mockito
 
Node.js Tutorial for Beginners | Node.js Web Application Tutorial | Node.js T...
Node.js Tutorial for Beginners | Node.js Web Application Tutorial | Node.js T...Node.js Tutorial for Beginners | Node.js Web Application Tutorial | Node.js T...
Node.js Tutorial for Beginners | Node.js Web Application Tutorial | Node.js T...
 
An introduction to Google test framework
An introduction to Google test frameworkAn introduction to Google test framework
An introduction to Google test framework
 
Web develop in flask
Web develop in flaskWeb develop in flask
Web develop in flask
 
Git commands
Git commandsGit commands
Git commands
 
Introduction to Gitlab | Gitlab 101 | Training Session
Introduction to Gitlab | Gitlab 101 | Training SessionIntroduction to Gitlab | Gitlab 101 | Training Session
Introduction to Gitlab | Gitlab 101 | Training Session
 
Spring boot Introduction
Spring boot IntroductionSpring boot Introduction
Spring boot Introduction
 
Threads in python
Threads in pythonThreads in python
Threads in python
 
Django Introduction & Tutorial
Django Introduction & TutorialDjango Introduction & Tutorial
Django Introduction & Tutorial
 
Laravel Design Patterns
Laravel Design PatternsLaravel Design Patterns
Laravel Design Patterns
 
A Basic Django Introduction
A Basic Django IntroductionA Basic Django Introduction
A Basic Django Introduction
 

Semelhante a Effective testing with pytest

Plone testingdzug tagung2010
Plone testingdzug tagung2010Plone testingdzug tagung2010
Plone testingdzug tagung2010Timo Stollenwerk
 
Making the most of your Test Suite
Making the most of your Test SuiteMaking the most of your Test Suite
Making the most of your Test Suiteericholscher
 
Grails unit testing
Grails unit testingGrails unit testing
Grails unit testingpleeps
 
Тестирование и Django
Тестирование и DjangoТестирование и Django
Тестирование и DjangoMoscowDjango
 
Mockito with a hint of PowerMock
Mockito with a hint of PowerMockMockito with a hint of PowerMock
Mockito with a hint of PowerMockYing Zhang
 
Pemrograman Python untuk Pemula
Pemrograman Python untuk PemulaPemrograman Python untuk Pemula
Pemrograman Python untuk PemulaOon Arfiandwi
 
Code igniter unittest-part1
Code igniter unittest-part1Code igniter unittest-part1
Code igniter unittest-part1Albert Rosa
 
Quality of life through Unit Testing
Quality of life through Unit TestingQuality of life through Unit Testing
Quality of life through Unit TestingSian Lerk Lau
 
Testing the frontend
Testing the frontendTesting the frontend
Testing the frontendHeiko Hardt
 
MT_01_unittest_python.pdf
MT_01_unittest_python.pdfMT_01_unittest_python.pdf
MT_01_unittest_python.pdfHans Jones
 
Working Effectively With Legacy Perl Code
Working Effectively With Legacy Perl CodeWorking Effectively With Legacy Perl Code
Working Effectively With Legacy Perl Codeerikmsp
 
Workshop 23: ReactJS, React & Redux testing
Workshop 23: ReactJS, React & Redux testingWorkshop 23: ReactJS, React & Redux testing
Workshop 23: ReactJS, React & Redux testingVisual Engineering
 
Automated Unit Testing
Automated Unit TestingAutomated Unit Testing
Automated Unit TestingMike Lively
 
Isolated development in python
Isolated development in pythonIsolated development in python
Isolated development in pythonAndrés J. Díaz
 
Fighting Fear-Driven-Development With PHPUnit
Fighting Fear-Driven-Development With PHPUnitFighting Fear-Driven-Development With PHPUnit
Fighting Fear-Driven-Development With PHPUnitJames Fuller
 

Semelhante a Effective testing with pytest (20)

UPC Testing talk 2
UPC Testing talk 2UPC Testing talk 2
UPC Testing talk 2
 
Plone testingdzug tagung2010
Plone testingdzug tagung2010Plone testingdzug tagung2010
Plone testingdzug tagung2010
 
Making the most of your Test Suite
Making the most of your Test SuiteMaking the most of your Test Suite
Making the most of your Test Suite
 
Grails unit testing
Grails unit testingGrails unit testing
Grails unit testing
 
Modern Python Testing
Modern Python TestingModern Python Testing
Modern Python Testing
 
Тестирование и Django
Тестирование и DjangoТестирование и Django
Тестирование и Django
 
Mockito with a hint of PowerMock
Mockito with a hint of PowerMockMockito with a hint of PowerMock
Mockito with a hint of PowerMock
 
Pemrograman Python untuk Pemula
Pemrograman Python untuk PemulaPemrograman Python untuk Pemula
Pemrograman Python untuk Pemula
 
Code igniter unittest-part1
Code igniter unittest-part1Code igniter unittest-part1
Code igniter unittest-part1
 
Quality of life through Unit Testing
Quality of life through Unit TestingQuality of life through Unit Testing
Quality of life through Unit Testing
 
Testing the frontend
Testing the frontendTesting the frontend
Testing the frontend
 
MT_01_unittest_python.pdf
MT_01_unittest_python.pdfMT_01_unittest_python.pdf
MT_01_unittest_python.pdf
 
Working Effectively With Legacy Perl Code
Working Effectively With Legacy Perl CodeWorking Effectively With Legacy Perl Code
Working Effectively With Legacy Perl Code
 
Unit testing - A&BP CC
Unit testing - A&BP CCUnit testing - A&BP CC
Unit testing - A&BP CC
 
Workshop 23: ReactJS, React & Redux testing
Workshop 23: ReactJS, React & Redux testingWorkshop 23: ReactJS, React & Redux testing
Workshop 23: ReactJS, React & Redux testing
 
Automated Unit Testing
Automated Unit TestingAutomated Unit Testing
Automated Unit Testing
 
Isolated development in python
Isolated development in pythonIsolated development in python
Isolated development in python
 
Fighting Fear-Driven-Development With PHPUnit
Fighting Fear-Driven-Development With PHPUnitFighting Fear-Driven-Development With PHPUnit
Fighting Fear-Driven-Development With PHPUnit
 
Unit test-using-spock in Grails
Unit test-using-spock in GrailsUnit test-using-spock in Grails
Unit test-using-spock in Grails
 
Unit testing
Unit testingUnit testing
Unit testing
 

Último

Artyushina_Guest lecture_YorkU CS May 2024.pptx
Artyushina_Guest lecture_YorkU CS May 2024.pptxArtyushina_Guest lecture_YorkU CS May 2024.pptx
Artyushina_Guest lecture_YorkU CS May 2024.pptxAnnaArtyushina1
 
WSO2CON 2024 Slides - Open Source to SaaS
WSO2CON 2024 Slides - Open Source to SaaSWSO2CON 2024 Slides - Open Source to SaaS
WSO2CON 2024 Slides - Open Source to SaaSWSO2
 
%in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park %in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park masabamasaba
 
WSO2Con2024 - Enabling Transactional System's Exponential Growth With Simplicity
WSO2Con2024 - Enabling Transactional System's Exponential Growth With SimplicityWSO2Con2024 - Enabling Transactional System's Exponential Growth With Simplicity
WSO2Con2024 - Enabling Transactional System's Exponential Growth With SimplicityWSO2
 
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfonteinmasabamasaba
 
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...Shane Coughlan
 
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park %in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park masabamasaba
 
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...masabamasaba
 
Architecture decision records - How not to get lost in the past
Architecture decision records - How not to get lost in the pastArchitecture decision records - How not to get lost in the past
Architecture decision records - How not to get lost in the pastPapp Krisztián
 
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...masabamasaba
 
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...chiefasafspells
 
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfonteinmasabamasaba
 
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...SelfMade bd
 
Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...
Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...
Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...Bert Jan Schrijver
 
WSO2CON 2024 - Does Open Source Still Matter?
WSO2CON 2024 - Does Open Source Still Matter?WSO2CON 2024 - Does Open Source Still Matter?
WSO2CON 2024 - Does Open Source Still Matter?WSO2
 
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...Jittipong Loespradit
 
%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrand%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrandmasabamasaba
 
WSO2Con204 - Hard Rock Presentation - Keynote
WSO2Con204 - Hard Rock Presentation - KeynoteWSO2Con204 - Hard Rock Presentation - Keynote
WSO2Con204 - Hard Rock Presentation - KeynoteWSO2
 
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrainmasabamasaba
 

Último (20)

Artyushina_Guest lecture_YorkU CS May 2024.pptx
Artyushina_Guest lecture_YorkU CS May 2024.pptxArtyushina_Guest lecture_YorkU CS May 2024.pptx
Artyushina_Guest lecture_YorkU CS May 2024.pptx
 
WSO2CON 2024 Slides - Open Source to SaaS
WSO2CON 2024 Slides - Open Source to SaaSWSO2CON 2024 Slides - Open Source to SaaS
WSO2CON 2024 Slides - Open Source to SaaS
 
%in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park %in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park
 
WSO2Con2024 - Enabling Transactional System's Exponential Growth With Simplicity
WSO2Con2024 - Enabling Transactional System's Exponential Growth With SimplicityWSO2Con2024 - Enabling Transactional System's Exponential Growth With Simplicity
WSO2Con2024 - Enabling Transactional System's Exponential Growth With Simplicity
 
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
 
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
 
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
 
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park %in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
 
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
 
Architecture decision records - How not to get lost in the past
Architecture decision records - How not to get lost in the pastArchitecture decision records - How not to get lost in the past
Architecture decision records - How not to get lost in the past
 
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
 
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
 
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
 
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
 
Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...
Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...
Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...
 
WSO2CON 2024 - Does Open Source Still Matter?
WSO2CON 2024 - Does Open Source Still Matter?WSO2CON 2024 - Does Open Source Still Matter?
WSO2CON 2024 - Does Open Source Still Matter?
 
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
 
%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrand%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrand
 
WSO2Con204 - Hard Rock Presentation - Keynote
WSO2Con204 - Hard Rock Presentation - KeynoteWSO2Con204 - Hard Rock Presentation - Keynote
WSO2Con204 - Hard Rock Presentation - Keynote
 
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
 

Effective testing with pytest

  • 1. Effective testing with Pytest @hectorcanto_dev slideshare.net/HectorCanto github.com/hectorcanto/pytest-samples
  • 2. Summary We will focus in Python testing with Pytest, but many things apply to any test framework in any language
  • 3. Why pytest IMHO pytest is a top-grade framework, updated, very well documented, and compatible with many tools.
  • 4. Why we need testing effectively Testing takes a good part of our time working on Software The best we test, the better software we make The most effective we are, more time to do other stuff
  • 5. Testing is very important Guarantees all our code works when adding new features Documents it with examples and context Debugging environment Enables refactoring Article: Tips and tricks for unit tests
  • 6. Effectivity means Running test as fast as possibly Developing and maintaining code Readable for everyone, especially newcomers
  • 7. How we achieve effectivity Using Pytest to its full potential Understanding artifacts: mocks, fixtures … Applying best practices Adding good libs and plugins Planning and common sense
  • 8. Test phases A test can be divided in 3 phases know as the triple A (AAA) Arrange Act Assert We will have points on every one of them
  • 9. Launching the suite Pytest options Markers Test selection Launch only what you need at every moment Pytest options We can filter tests in several ways pytest --help pytest tests/folder/test_file.py::test_name pytest -m smoke pytest -k users
  • 10. Suite: repeat failed tests We will give priority to failed ones Suite: specific tests Naming is key pytest --fail-first pytest --last-failed pytest --failed-first def test_users_creation(): ... def test_users_update(): ... pytest -k users
  • 11. Suite: markers Useful markers: smoke, unit, integration, current, slow @pytest.mark.slow @pytest.mark.current @pytest.mark.skip(reason="whatever") @pytest.mark.xfail def test_this(): ... pytest -m current -s -v pytest -m "slow and not integration" pytest -m "smoke and unit"
  • 12. Suite: global markers It is easy to mark groups of tests # Per module pytestmark = pytest.mark.auth pytestmark = [pytest.mark.deletion, pytest.mark.api] # Per class class TestClass: pytestmark = pytest.mark.special
  • 13. Suite: Folder structure tests ├── smoke/ ├── unit/ ├── integration/ ├── api/ ├ ├── fixtures/ ├── factories/ ├── conftest.py ├── aux.py └── __init__.py
  • 14. Suite: automatic markers Auto-mark tests by folder at tests/conftest.py def pytest_collection_modifyitems(items): for item in items: if "/smoke/" in str(item.module): item.add_marker("smoke")
  • 15. Launching the suite: test types Smoke tests: very simple ones. Run first Unit test: small and independent. Run often Integration: they need something external. Run sometimes API: outside the box. Run sometimes
  • 16. Suite: ordering Ordering is useful for smoke tests and on CI pipeline Randomization helps finding lateral effects @pytest.mark.first @pytest.mark.second @pytest.mark.last Plugins: pytest-ordering, pytest-randomly
  • 17. Suite: environment We keep local and test envs apart # setup.cfg [tool:pytest] env = PYTHONBREAKPOINT=ipdb.set_trace APP_ENVIRONMENT=test CACHE=memory DEBUG=1 VAR=value Plugin: pytest-env
  • 18. Suite: environment 2 def test_with_different_env_vars(monkeypatch): monkeypatch.setenv("CACHE", "nocache") monkeypatch.delenv("VAR") Fixture: *monkeypatch*
  • 20. Setup: Parametrization Instead of making a test for each input <ake one test for all inputs @pytest.mark.parametrize("entry, expected", ( (1, True), (2, False), (None, False) ("hello", False) ) def test_something(entry, expected: bool): result = function_under_test(entry) assert result == expected
  • 21. Fixtures In general, fixtures are the artifacts all around a test Synthetic data used as input The system in a specific state Active elements that interact with the SUT
  • 22. Pytest fixtures In Pytest, fixtures are created with an especial decorator There might be fixtures with setup, teardown or both @pytest.fixture def example_fixture() now = datetime.utcnow() do_setup(now) return now do_teardown()
  • 23. Fixture example @pytest.fixture def load_data(db_client): my_user = User(name="Hector", last_name="Canto") db_client.add(my_user) yield my_user # usable as parameter db_client.delete(my_user)
  • 24. Fixture usage Place your fixtures on any conftest.py No need to import your fixtures @pytest.mark.usefixtures("load_data") def test_with_fixtures(example_fixture): result, error = system_under_tests(example_fixture) assert result assert not error
  • 25. Reuse fixtures Scopes: session, module, class or function @pytest.fixture(scope="session") def test_settings(): yield get_settings(test=True)
  • 26. Automatic fixtures It always runs It depends on another fixture @pytest.fixture(autouse=True) def clean_db(db_client): yield db_client for table in db_client.tables: db_client.truncate()
  • 27. Data fixtures Import constants Generation functions Factories No raw json, text files … Create your data fixture programmatically
  • 28. Setup: Factory from factory import StubFactory class ObjectFactory(StubFactory): name: str = "value" my_ojb = ObjectFactory() assert my_obj.name == "value" other = ObjectFactory(name="another value") assert other.name == "another value" Libary & plugin: factoryboy, pytest-factoryboy
  • 29. Factory & Faker Generate multiple values in one go from factoryboy import Faker, SelfAttribute from factory.fuzzy import FuzzyInteger class ObjectFactory(StubFactory): name: str = Faker("first_name_male") last_name: str = Faker("last_name") full_name: str = SelfAttribute(lambda self: f"{self.name} {se phone: str = Faker("phone_number", local="en_GB") money: int = FuzzyInteger(1, 1000) Library: Faker
  • 30. Model Factory Can be used with Django, SQLAlchemy and Pymongo from factory import alchemy, RelatedFactory class UserFactory(alchemy.SQLAlchemyModelFactory): class Meta: model = User sqlalchemy_session = Session id = LazyFunction(lambda: randint(1, 1_000_000)) location = RelatedFactory(LocationFactory) created_at = Faker( "unix_time", start_datetime=datetime(2015, 1, 1), end_datetime=datetime(2019, 12, 31) )
  • 31. Batches & Dict Factory class UserDictFactory(DictFactory) … factory.build(dict, FACTORY_CLASS=UserFactory) UserFactory.create_batch(5) UserFactory.build_batch(5) # created but not persisted Generate _inputs_ for an API or a function <aside class="notes"> </aside>
  • 32. Execution or Act Control Time Test doubles: Mocks and other Interceptors
  • 33. Freezegun from datetime import datetime from freezegun import freeze_time @freeze_time("2012-01-14") def test_2012(): assert datetime.now() == datetime(2012, 1, 14) with freeze_time("2018-01-01"): assert datetime.now() > datetime(2012, 1, 4) Library: freezegun
  • 34. Freezer from datetime import datetime def test_freezer_move(freezer): now = datetime.now() freezer.move_to('2017-05-20') later = datetime.now() assert now != later @pytest.mark.freeze_time('2017-05-21') def test_freeze_decorator(): assert datetime.now() == datetime(2017, 5, 21) Fixture: freezer
  • 35. Other date and time libraries timeago - moment - pytime - arrow timeago.format(timedelta(seconds = 60 * 3.4)) # 3 minutes ago moment.date(2012, 12, 19).add(hours=1, minutes=2, seconds=3) # n pytime.next_month('2015-10-1') # again, no-more-deltas arrow.utcnow().span('hour') # 2 datetimes in one line
  • 36. Execution: test doubles Doubles replace some element to provide the desired behaviour
  • 38. Mock library In python most doubles are implemented by the mock library Library: mock - Plugin: pytest-mock - Fixture: mocker
  • 39. Python mocks def test_mock_patching(mocker): url = "https://2021.es.pycon.org/" mocked = mocker.patch.object(requests, "get", return_value="i mocker.patch.object(requests, "post", side_effect=ForbiddenEr response = requests.get(url) assert response == "intercepted" assert mocked.called_once()
  • 40. Stubs def test_stubbing(monkeypatch): def mock_exist(value): print(f"{value} exists") return True monkeypatch.setattr(os.path, 'exists', mock_exist) assert os.path.exists("/believe/me/I/exist")
  • 41. Spies It does not intercept, only registers callbacks def test_with_spy(mocker): url = "https://2021.es.pycon.org/" spy = mocker.spy(requests, "get") response = requests.get(url) assert response.status_code -= 200 spy.assert_called_once(), spy.mock_calls spy.assert_called_with(url)
  • 42. Interceptors Intercepts callbacks, returns what you need def test_with_http_interceptor(requests_mock): # arranges url = "http://tests.com" requests_mock.get(url, json={"key": "value"}) # action response = requests.get(url) assert "key" in response.json() Libray: requests_mock
  • 43. Validation or assert def test_one(): expected = 5 result = system_under_test() assert result assert result is not None assert result == expected assert result > 3
  • 44. Assertion error messages In case of AssertionError we expose some extra information response = requests.get(url) assert response.json() == expected, response.text()
  • 45. Comparisons Don’t lose time on unimportant differences import pytest, math assert 2.2 == pytest.approx(2.3, 0.1) assert math.isclose(2.2, 2.20001, rel_tol=0.01)
  • 46. Comparisons: list and sets Don’t lose your head around repeated values nor with loops comparing list my_list = [1, 1, 2, 3, 4] other_list = [4, 3, 2, 1] list_without_duplicates = list(set(my_list)) diff = set(my_list) ^ set(other_list) assert not diff
  • 47. Comparison: dicts Do not lose time ordering responses Ignore painful fields like dates from deepdiff import DeepDiff def test_dicts(parameter, expected): result = system_under_test(parameter) diff = DeepDiff(result, expected, exclude_paths=(f"root['upda assert not diff, diff Library: deepdiff
  • 48. Assert at the end def test_delayed_response(requests_mock): url = "http://tests.com" requests_mock.get(url, json={"key": "value"}) response = requests.get(url) expect(response.status_code == 200, response.status_code) expect(response.json() == {}, response.text) assert_expectations() Library: delayed-assert
  • 49. Delayed assert prompt def assert_expectations(): 'raise an assert if there are any failed expectations' if _failed_expectations: > assert False, _report_failures() E AssertionError: E E assert_expectations() called at E "/home/hector/Code/personal/effective_testing/tests/u E E Failed Expectations : 1 E E 1: Failed at "/home/hector/Code/personal/effective_te E ErrorMessage: {"key": "value"} E expect(response.status_code == 200, response.stat ../../../.local/share/virtualenvs/effective testing-QL w0uj8/lib/
  • 50. Log validation Log are key for several reason Monitoring and metrics Treaceability and error control Especially important on microservices and serverless
  • 51. Log capture def test_log_capture(request, caplog): logger = logging.getLogger(request.node.name) caplog.set_level("INFO") dtt = "2021-10-02 10:55:00" msg = "captured message" expected_message = f"{dtt} INFO module:{request.node.name} {m with freeze_time(dtt): logger.info("captured message") with caplog.disabled(): logger.info ("Any log here will not be captured") some_function_with_logs() breakpoint() with freeze_time(dtt): logger.info("captured message") Fixture: caplog, capsys, capfd
  • 52. Extra ball: chasing errors Tests are little environments you can reuse to look for unexpected errors Debugging in live servers is not always possible nor easy Library: ipdb, IDEs pip install ipdb PYTHONBREAKPOINT=ipdb.set_trace pytest -m current -s Use your IDE built-in breakpoints and debugger
  • 53. Anti-patterns Too many mocks Fighting the framework Sequential tests Dirty sources Test Python or a library https://www.softwaretestingmagazine.com/knowledg patterns-in-software-testing/ https://dzone.com/articles/unit-testing-anti- patterns-full-list
  • 54. Final ideas “Tests are a waste of time” Actually they save you a lot of time Treat tests as first-class citizens: style, docstrings, comments … There is no small project that is worth having tests Think on your peers and your future self Remember the test pyramdg, don’t put all your stakes in one type of tests
  • 55. Recommendations If something is taking too long to do there’s a library or a recipe that can help you Read and revisit the documentation you will always find something new Frameworks help you, check their test documentation
  • 56. Summary Fixtures: monkeypatch, mocker, requests_mock, caplog, parametrize, mark Libraries: factoryboy, faker, deepdict, freezegun, moment, ipdb https://docs.pytest.org/ https://docs.pytest.org/en/latest/reference/plugin_lis
  • 57. Thanks! Thanks for your attention, hope you like it Any question, suggestion … ? https://github.com/hectorcanto/pytest-samples https://www.slideshare.net/HectorCant