あいつの日誌β

働きながら旅しています。

py.test の書き方 備忘録

Pytestの備忘録

py.test の書き方について重要なところだけまとめてみました。 大体以下のことがつかめればOKだと思います。

  • autouse と pytest.ini の関係
  • scope の使い方
  • request.addfinalizer

autouse

tests/test_my.py を用意

# -*- coding: utf-8 -*-
import pytest

@pytest.fixture()
def create_fixture():
    print '[create_fixture]'

class TestMy(object):
    
    def test_101(self):
        print '[test_101]'
    
    def test_102(self, create_fixture):
        print '[test_102]'
    
    def test_103(self):
        print '[test_103]'

py.test を実行(表示結果を少し見やすく加工しています)

% py.test -s tests/test_my.py
[test_101]
[create_fixture]
[test_102]
[test_103]

test_002 だけ create_fixture が実行されています。すべてのテストで実行する場合は以下のように autouse を使います。

- @pytest.fixture()
+ @pytest.fixture(autouse=True)

もしはく pytest.ini に記述をしても OK です。

[pytest]
usefixtures = create_fixture

実行結果は以下の通り

[create_fixture]
[test_101]
[create_fixture]
[test_102]
[create_fixture]
[test_103]

scope

tests/test_my.py に以下を追加

class TestMy200(object):
        
    def test_201(self):
        print '[test_201]'
        
    def test_202(self):
        print '[test_202]'
        
    def test_203(self):
        print '[test_203]'

実行結果

[create_fixture]
[test_101]
[create_fixture]
[test_102]
[create_fixture]
[test_103]
[create_fixture]
[test_201]
[create_fixture]
[test_202]
[create_fixture]
[test_203]

sope を module に変更します

@pytest.fixture(autouse=True, scope='module')

実行結果

[create_fixture]
[test_101]
[test_102]
[test_103]
[test_201]
[test_202]
[test_203]

scope を class, session 単位に変更することもできます。

具体的な例

以下は redis をテストする例です。 fixture 関数で return しておくと、各関数の引数に fixutre 関数と同名の引数を指定しておくと帰り値を利用することができます。

また request.addfinalizer を利用すると、スコープから外れるタイミングで関数を実行させる事ができます。

# -*- coding: utf-8 -*-
import pytest
import redis
import tcptest.redis

@pytest.fixture(autouse=True, scope='module')
def redis_server(request):
    print '[create redis server]'
    server = tcptest.redis.Server()    
    server.start()
 
    def stop():
        server.stop()
    request.addfinalizer(stop)
    return server  # flushall_redis_server で server.port を利用するため return

@pytest.fixture(autouse=True, scope='function')
def flushall_redis_server(redis_server):
    print '[flushall redis server]'
    redis.Redis(port=redis_server.port).flushall()

class TestMy100(object):
    
    def test_101(self):
        print '[test_101]'

    def test_102(self):
        print '[test_102]'

    def test_103(self):
        print '[test_103]'
        
        
class TestMy200(object):

    def test_201(self):
        print '[test_201]'

    def test_202(self):
        print '[test_202]'

    def test_203(self):
        print '[test_203]'

実行結果

[create redis server]
[flushall redis server]
[test_101]
[flushall redis server]
[test_102]
[flushall redis server]
[test_103]
[flushall redis server]
[test_201]
[flushall redis server]
[test_202]
[flushall redis server]
[test_203]

応用問題

最後に私がはまった問題を紹介します。お時間がある方は下記のコードが実行される順番を予想してみてください。

# -*- coding: utf-8 -*-
import pytest

@pytest.fixture(scope='module')
def mod():
    print '[module]'

@pytest.fixture(autouse=True, scope='class')
def cls():
    print '[class]'

@pytest.fixture(autouse=True, scope='function')
def func(mod):
    print '[function]'

class TestMy100(object):

    def test_101(self):
        print '[test_101]'
    
    def test_102(self):
        print '[test_102]'
    
    def test_103(self):
        print '[test_103]'

class TestMy200(object):
 
    def test_201(self):
        print '[test_201]'
    
    def test_202(self):
        print '[test_202]'
    
    def test_203(self):
        print '[test_203]'

実行結果

[class]    # class が先に実行されている
[module]
[function]
[test_101]
[function]
[test_102]
[function]
[test_103]
[class]
[function]
[test_201]
[function]
[test_202]
[function]
[test_203]

関数が実行される順番は一見このように見えるかもしれません

  • mod(autouse=False, scope='module')
  • cls(autouse=True, scope='class')
  • func(autouse=True, scope='function')

しかし、実際はこうなります。

  • cls(autouse=True, scope='class')
  • mod(autouse=False, scope='module')
  • func(autouse=True, scope='function')

このケースだと autouse=False の mod が実行されるタイミングが func が実行されたタイミング(引数に mod が指定されているから) になっているところがポイントです。 でもスコープは保たれているので mod 関数は1回、cls 関数は2回呼ばれています。

以上 py.test のよくある書き方でした。