[PYTHON] I tried to put pytest into the actual battle

Feature

--You can write setup code using conftest.py (can be created for each directory) --Abundant hook functions --You can pass data to test functions using DI-like features --Used to create and get the object to be tested --It is possible to pass a value to a test function using a decorator --Pass preset values, expected test values, etc. --You don't have to create a class to write test code like unittest --Result output is easy to see --Easy to skip tests when removing features or seeing off when the app is released.

This is what it means to write conftest.py for each directory.

tests
├── __init__.py
├── event
│   ├── __init__.py
|   |── conftest.py         #Common setup code
│   ├── battle_event_1207
│   │   ├── __init__.py
│   │   ├── conftest.py     #Limited time event setup code
│   │   └── test_event.py
│   └── battle_event_1213
│       ├── __init__.py
│       ├── conftest.py     #Another one
│       └── test_event.py
└── logics
    └── __init__.py

How you use it

__ The web framework uses Flask __

--Testing the logic part of Model (excluding writing to the datastore: do not commit or use Mock) --Testing the utility function part

Unused part

Where it is impossible unless you understand the application specifications and learn how to use the web framework / test framework

--The part where requests such as Controller are involved or straddle --The part that is useless unless you prepare a test data store --Transaction-related part

_ Considering whether to use Web Test or Flask's built-in Test _

sample

Setup code

It seems to be good to import or initialize the test target. If you create a file with the name conftest.py, pytest will read it for you.

Also, the functions starting with pytest_ are special functions and mainly usepytest_funcarg__xxxx (request). You can use this to pass objects that depend on the test function as arguments. On the test function side, if you specify an argument with the name xxxx, it will be passed there.

conftest.py


# coding: utf-8
"""battle_event_1213 bootstrap.

Common processing of modules used in test code
Creation of test targets, etc.

"""

from datetime import datetime, timedelta


def pytest_report_header(config):
    """Test report header
    """
    return "battle_event_1213 test"


def get_time_subtract_by_delta(seconds):
    return datetime.now() + timedelta(seconds=-1 * seconds)


#def pytest_addoption(parser):
#    parser.addoption('--ssh', action='store', default=None, help='specify ssh host to run tests with')


class MySetup(object):
    """Factory described here
    """
    def getEventCls(self):
        from event_models.battle_event_1213 import Event
        return Event

    def makeEvent(self, *args, **kwargs):
        return self.getEventCls()(*args, **kwargs)


def pytest_funcarg__mysetup(request):
    """DI(Dependency injection)
Test function/In the method argument`mysetup`To specify
    """
    return MySetup()


def pytest_funcarg__event(request):
    return MySetup().makeEvent()

Test code

You can use the pytest.mark.skipif decorator to skip the test when the condition is true. Although it is a function that has already been deleted, it is possible to leave only the test case for the time being, or to specify the expiration date and skip the function for a limited time when the time comes. On the setup code side, the object returned by the function pytest_funcarg__event (request) is passed to the ʻeventargument. Since you want to test the state of the object to be tested that is basically set up from the beginning, usingconftest.pyeliminates the trouble of setting up on the test code side. As in this example, you can use thepytest.mark.parametrizedecorator to pass input and expected values to eliminate the hassle of repeatedly writing test functions / methods. It is passed as an argument to the test function, just likepytest_funcarg__xxxx (request)`.

test_event.py


# coding: utf-8
import pytest
from datetime import datetime


#Test validity period
is_timeout_event = datetime.now() > datetime(2012,12,19,23,59)
check_event_period = pytest.mark.skipif('%s' % is_timeout_event)

#Test parameters
EXCEEDED_GAUGE = 140
MAX_GAUGE = 100
MAX_ATTACK_PT = 4


#Skip the test if you put some characters in skipif(I deleted the bad function, so I skipped the test)
@pytest.mark.skipif('"The gauge uses a great gauge so skip this test"')
class TestEventDame(object):
    @pytest.mark.parametrize('prop', [
        'dame_gauge',
    ])
    def test_exists_properties(self, event, prop):
        assert hasattr(event, prop)

    @pytest.mark.parametrize(('input_val', 'set_val', 'mode'), [
        (0, 0, False),
        (1, 1, False),
        (MAX_GAUGE, MAX_GAUGE, True),
        (EXCEEDED_GAUGE, MAX_GAUGE, True),
    ])
    def test_update_dame_gauge(self, event, input_val, set_val, mode):
        event.update_dame_gauge(input_val)
        assert event.dame_gauge == set_val
        assert event.is_dame() is mode

    @pytest.mark.parametrize(('value', 'expected'), [
        (0, 0),
        (1, 1),
        (2, 2),
        (3, 3),
        (4, 6),
    ])
    def test_get_attack_pt_effect(self, event, value, expected):
        result = event.get_attack_pt_effect(value)
        assert result == expected


#Skip this test at the end of the period
@check_event_period
class TestEventSugoiGauge(object):
    @pytest.mark.parametrize('prop', [
        'attack_pt',
        'current_use_attack_pt',
        'update_datetime',
        '_use_full_attack_pt_count',
        '_use_full_attack_pt_count_with_win',
    ])
    def test_exists_properties(self, event, prop):
        assert hasattr(event, prop)

    @pytest.mark.parametrize(('value', 'expected'), [
        (0, False),
        (1, False),
        (2, False),
        (3, False),
        (4, True),
    ])
    def test_is_max_attack_pt(self, event, value, expected):
        assert event.is_max_attack_pt(value) is expected

    @pytest.mark.parametrize(('value', 'expected'), [
        (0, 1),
        (1, 2),
        (9, 10),
    ])
    def test_inc_use_full_attack_pt_count(self, event, value, expected):
        event._use_full_attack_pt_count = value
        event._use_full_attack_pt_count_with_win = value
        event.inc_use_full_attack_pt_count()
        event.inc_use_full_attack_pt_count_with_win()
        assert event._use_full_attack_pt_count == expected
        assert event._use_full_attack_pt_count_with_win == expected

    @pytest.mark.parametrize(('value', 'win_flag', 'expected', 'win_expected'), [
        (0, True, 0, 0),
        (1, True, 0, 0),
        (3, True, 0, 0),
        (MAX_ATTACK_PT, False, 1, 0),
        (MAX_ATTACK_PT, True, 1, 1),
    ])
    def test_set_use_full_attack_pt(self, event, value, win_flag, expected, win_expected):
        event.set_use_full_attack_pt(value, is_win=win_flag)
        assert event._use_full_attack_pt_count == expected
        assert event._use_full_attack_pt_count_with_win == win_expected


if __name__ == '__main__':
    pytest.main()

Conclusion

I can't go back to unittest

reference

-Translated pytest documentation

-Welcome to Pytest

Recommended Posts

I tried to put pytest into the actual battle
I tried to move the ball
I tried to estimate the interval.
I tried to divide the file into folders with Python
I tried to summarize the umask command
I tried to recognize the wake word
I tried to summarize the graphical modeling.
I tried to estimate the pi stochastically
I tried to touch the COTOHA API
Matching karaoke keys ~ I tried to put it on Laravel ~ <on the way>
I tried web scraping to analyze the lyrics.
I tried to optimize while drying the laundry
I tried to save the data with discord
I tried to touch the API of ebay
I tried to correct the keystone of the image
Qiita Job I tried to analyze the job offer
LeetCode I tried to summarize the simple ones
I tried to implement the traveling salesman problem
I tried to predict the price of ETF
I tried to vectorize the lyrics of Hinatazaka46!
I tried to debug.
I tried to paste
I tried to learn the sin function with chainer
I tried to graph the packages installed in Python
I tried to detect the iris from the camera image
I tried to touch the CSV file with Python
I tried to predict the J-League match (data analysis)
I tried to solve the soma cube with python
I tried to approximate the sin function using chainer
[Python] I tried to graph the top 10 eyeshadow rankings
I tried to visualize the spacha information of VTuber
I tried to erase the negative part of Meros
I tried to solve the problem with Python Vol.1
I tried to simulate the dollar cost averaging method
I tried to redo the non-negative matrix factorization (NMF)
I tried to identify the language using CNN + Melspectogram
I tried to notify the honeypot report on LINE
I tried to complement the knowledge graph using OpenKE
I tried to classify the voices of voice actors
I tried to compress the image using machine learning
I tried to summarize the string operations of Python
I tried to put out the frequent word ranking of LINE talk with Python
I tried to put HULFT IoT (Agent) in the gateway Rooster of Sun Electronics
[Uncorrelated test] I tried to put out the boundary line with or without rejection
I tried to learn PredNet
I tried to implement PCANet
I tried the changefinder library!
I tried to reintroduce Linux
I tried to introduce Pylint
I tried to summarize SparseMatrix
I tried to touch jupyter
I tried to implement StarGAN (1)
I tried to find the entropy of the image with python
I tried to find out the outline about Big Gorilla
I tried to introduce the block diagram generation tool blockdiag
[Horse Racing] I tried to quantify the strength of racehorses
I tried to simulate how the infection spreads with Python
I tried to analyze the whole novel "Weathering with You" ☔️
[First COTOHA API] I tried to summarize the old story
I tried to find the average of the sequence with TensorFlow
I tried to notify the train delay information with LINE Notify