
.. highlightlang:: python

基本的なパターンと例
====================

..
    Basic patterns and examples
    ==========================================================

..
    Pass different values to a test function, depending on command line options
    ----------------------------------------------------------------------------

コマンドラインオプションでテスト関数に違う値を渡す
--------------------------------------------------

.. regendoc:wipe

..
    Suppose we want to write a test that depends on a command line option.
    Here is a basic pattern how to achieve this::

コマンドラインオプションで制御するテストを書きたいと仮定します。これを実現する基本的な方法は次の通りです::

    # test_sample.py の内容
    def test_answer(cmdopt):
        if cmdopt == "type1":
            print ("first")
        elif cmdopt == "type2":
            print ("second")
        assert 0 # 何が表示されるかを見るため


..
    For this to work we need to add a command line option and
    provide the ``cmdopt`` through a :ref:`function argument <funcarg>` factory::

このためにはコマンドラインオプションを追加する必要があります。 :ref:`関数の引数 <funcarg>` ファクトリーを使って ``cmdopt`` を提供します::

    # conftest.py の内容
    def pytest_addoption(parser):
        parser.addoption("--cmdopt", action="store", default="type1",
            help="my option: type1 or type2")

    def pytest_funcarg__cmdopt(request):
        return request.config.option.cmdopt

..
    Let's run this without supplying our new command line option::

先ほど作成したコマンドラインオプションを指定せずに実行してみましょう::

    $ py.test -q test_sample.py
    collecting ... collected 1 items
    F
    ================================= FAILURES =================================
    _______________________________ test_answer ________________________________
    
    cmdopt = 'type1'
    
        def test_answer(cmdopt):
            if cmdopt == "type1":
                print ("first")
            elif cmdopt == "type2":
                print ("second")
    >       assert 0 # 何が表示されるかを見るため
    E       assert 0
    
    test_sample.py:6: AssertionError
    ----------------------------- Captured stdout ------------------------------
    first
    1 failed in 0.01 seconds

..
    And now with supplying a command line option::

次はコマンドラインオプションを指定して実行します::

    $ py.test -q --cmdopt=type2
    collecting ... collected 1 items
    F
    ================================= FAILURES =================================
    _______________________________ test_answer ________________________________
    
    cmdopt = 'type2'
    
        def test_answer(cmdopt):
            if cmdopt == "type1":
                print ("first")
            elif cmdopt == "type2":
                print ("second")
    >       assert 0 # 何が表示されるかを見るため
    E       assert 0
    
    test_sample.py:6: AssertionError
    ----------------------------- Captured stdout ------------------------------
    second
    1 failed in 0.01 seconds

..
    Ok, this completes the basic pattern.  However, one often rather
    wants to process command line options outside of the test and
    rather pass in different or more complex objects.  See the
    next example or refer to :ref:`mysetup` for more information
    on real-life examples.

はい。基本的な使い方が分かりました。これ以外にも、テストの外部でコマンドラインオプションを処理して、別オブジェクトや複雑なオブジェクトを渡したいこともよくあります。次の例、もしくは現実の世界での例は :ref:`mysetup` を参照してください。

..
    Dynamically adding command line options
    --------------------------------------------------------------

コマンドラインオプションを動的に追加
------------------------------------

.. regendoc:wipe

..
    Through :confval:`addopts` you can statically add command line
    options for your project.  You can also dynamically modify
    the command line arguments before they get processed::

:confval:`addopts` を使って、プロジェクトにコマンドラインオプションを静的に追加できます。静的に追加したコマンドラインオプションが処理される前に、そのコマンドラインオプションを動的に変更することもできます::

    # conftest.py の内容
    import sys
    def pytest_cmdline_preparse(args):
        if 'xdist' in sys.modules: # pytest-xdist プラグイン
            import multiprocessing
            num = max(multiprocessing.cpu_count() / 2, 1)
            args[:] = ["-n", str(num)] + args

..
    If you have the :ref:`xdist plugin <xdist>` installed
    you will now always perform test runs using a number
    of subprocesses close to your CPU. Running in an empty
    directory with the above conftest.py::

:ref:`xdist プラグイン <xdist>` をインストール済みなら、毎回 CPU 数に近いサブプロセスを使ってテストを実行できます。空のディレクトリで上記の conftest.py を実行します::

    $ py.test
    =========================== test session starts ============================
    platform linux2 -- Python 2.7.1 -- pytest-2.2.4
    gw0 I / gw1 I / gw2 I / gw3 I
    gw0 [0] / gw1 [0] / gw2 [0] / gw3 [0]
    
    scheduling tests via LoadScheduling
    
    =============================  in 0.52 seconds =============================

.. _`excontrolskip`:

コマンドラインオプションでテストのスキップを制御
------------------------------------------------

..
    Control skipping of tests according to command line option
    --------------------------------------------------------------

.. regendoc:wipe

..
    Here is a ``conftest.py`` file adding a ``--runslow`` command
    line option to control skipping of ``slow`` marked tests::

``slow`` とマークされたテストのスキップを制御するコマンドラインオプション ``--runslow`` を追加する ``conftest.py`` があります::

    # conftest.py の内容

    import pytest
    def pytest_addoption(parser):
        parser.addoption("--runslow", action="store_true",
            help="run slow tests")

    def pytest_runtest_setup(item):
        if 'slow' in item.keywords and not item.config.getvalue("runslow"):
            pytest.skip("need --runslow option to run")

..
    We can now write a test module like this::

テストモジュールは次のように書きます::

    # test_module.py の内容

    import pytest
    slow = pytest.mark.slow

    def test_func_fast():
        pass

    @slow
    def test_func_slow():
        pass

..
    and when running it will see a skipped "slow" test::

実行すると、"slow" テストがスキップされます::

    $ py.test -rs    # "-rs" は 's' の詳細をレポートします
    =========================== test session starts ============================
    platform linux2 -- Python 2.7.1 -- pytest-2.2.4
    collecting ... collected 2 items
    
    test_module.py .s
    ========================= short test summary info ==========================
    SKIP [1] /tmp/doc-exec-225/conftest.py:9: need --runslow option to run
    
    =================== 1 passed, 1 skipped in 0.01 seconds ====================

..
    Or run it including the ``slow`` marked test::

もしくは ``slow`` とマークされたテストを実行します::

    $ py.test --runslow
    =========================== test session starts ============================
    platform linux2 -- Python 2.7.1 -- pytest-2.2.4
    collecting ... collected 2 items
    
    test_module.py ..
    
    ========================= 2 passed in 0.01 seconds =========================

..
    Writing well integrated assertion helpers
    --------------------------------------------------

統合的なアサーションヘルパーの作成
----------------------------------

.. regendoc:wipe

..
    If you have a test helper function called from a test you can
    use the ``pytest.fail`` marker to fail a test with a certain message.
    The test support function will not show up in the traceback if you
    set the ``__tracebackhide__`` option somewhere in the helper function.
    Example::

テストから呼ばれるテストヘルパー関数があるなら、特定メッセージ付きでテストを失敗させる ``pytest.fail`` マーカーを使えます。 ``__tracebackhide__`` オプションをヘルパー関数内にセットすると、そのテストヘルパー関数はトレースバックを表示しなくなります。サンプルを紹介します::

    # test_checkconfig.py の内容
    import pytest
    def checkconfig(x):
        __tracebackhide__ = True
        if not hasattr(x, "config"):
            pytest.fail("not configured: %s" %(x,))

    def test_something():
        checkconfig(42)

..
    The ``__tracebackhide__`` setting influences py.test showing
    of tracebacks: the ``checkconfig`` function will not be shown
    unless the ``--fulltrace`` command line option is specified.
    Let's run our little function::

``__tracebackhide__`` 設定は、py.test のトレースバック表示に影響を与えます。 ``checkconfig`` 関数は、 ``--fulltrace`` コマンドラインオプションを指定しない限り、トレースバックを表示しません。この小さな関数を実行してみましょう::

    $ py.test -q test_checkconfig.py
    collecting ... collected 1 items
    F
    ================================= FAILURES =================================
    ______________________________ test_something ______________________________
    
        def test_something():
    >       checkconfig(42)
    E       Failed: not configured: 42
    
    test_checkconfig.py:8: Failed
    1 failed in 0.01 seconds

..
    Detect if running from within a py.test run
    --------------------------------------------------------------

py.test で実行していることを検出
--------------------------------

.. regendoc:wipe

..
    Usually it is a bad idea to make application code
    behave differently if called from a test.  But if you
    absolutely must find out if your application code is
    running from a test you can do something like this::

通常は、テストから呼ばれる場合にアプリケーションコードの振る舞いを分けるのは悪い考えです。しかし、アプリケーションコードがテストから実行されている場合に、確実に解明しなければならないことがあるなら、次のようなことができます::

    # conftest.py の内容

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

    def pytest_unconfigure(config):
        del sys._called_from_test

..
    and then check for the ``sys._called_from_test`` flag::

アプリケーション内で ``sys._called_from_test`` というフラグをチェックします::

    if hasattr(sys, '_called_from_test'):
        # テスト内から実行時に呼ばれる
    else:
        # "普通" のときに呼ばれる

..
    accordingly in your application.  It's also a good idea
    to use your own application module rather than ``sys``
    for handling flag.

フラグを処理するために ``sys`` よりも独自のアプリケーションモジュールを使うのも良い考えです。

..
    Adding info to test report header
    --------------------------------------------------------------

テストレポートヘッダーに情報を追加
----------------------------------

.. regendoc:wipe

..
    It's easy to present extra information in a py.test run::

py.test の実行時に追加の情報を表示するのは簡単です::

    # conftest.py の内容
    
    def pytest_report_header(config):
        return "project deps: mylib-1.1"

..
    which will add the string to the test header accordingly::

この関数はテストヘッダーに文字列を追加します::

    $ py.test
    =========================== test session starts ============================
    platform linux2 -- Python 2.7.1 -- pytest-2.2.4
    project deps: mylib-1.1
    collecting ... collected 0 items
    
    =============================  in 0.00 seconds =============================

.. regendoc:wipe

..
    You can also return a list of strings which will be considered as several
    lines of information.  You can of course also make the amount of reporting
    information on e.g. the value of ``config.option.verbose`` so that
    you present more information appropriately::

複数行に渡る情報を扱うなら文字列のリストも返せます。当然レポートの情報量も制御できます。例えば、必要なときに情報を表示するために ``config.option.verbose`` の値で切り分けます::

    # conftest.py の内容

    def pytest_report_header(config):
        if config.option.verbose > 0:
            return ["info1: did you know that ...", "did you?"]

..
    which will add info only when run with "--v"::

"--v" を指定して実行したときのみ追加の情報が表示されます::

    $ py.test -v
    =========================== test session starts ============================
    platform linux2 -- Python 2.7.1 -- pytest-2.2.4 -- /home/hpk/venv/0/bin/python
    info1: did you know that ...
    did you?
    collecting ... collected 0 items
    
    =============================  in 0.00 seconds =============================

..
    and nothing when run plainly::

何も指定せずに実行すると何も表示しません::

    $ py.test
    =========================== test session starts ============================
    platform linux2 -- Python 2.7.1 -- pytest-2.2.4
    collecting ... collected 0 items
    
    =============================  in 0.00 seconds =============================

..
    profiling test duration
    --------------------------

テスト実行のプロファイリング
----------------------------

.. regendoc:wipe

.. versionadded: 2.2

..
    If you have a slow running large test suite you might want to find
    out which tests are the slowest. Let's make an artifical test suite::

巨大なテストスイートの実行に時間がかかる場合、どのテストが最も遅いかを調べたいときがあります。擬似テストスイートで試してみましょう::

    # test_some_are_slow.py の内容

    import time

    def test_funcfast():
        pass

    def test_funcslow1():
        time.sleep(0.1)

    def test_funcslow2():
        time.sleep(0.2)

..
    Now we can profile which test functions execute the slowest::

次にようにして、どのテスト関数が最も遅いかをプロファイルできます::

    $ py.test --durations=3
    =========================== test session starts ============================
    platform linux2 -- Python 2.7.1 -- pytest-2.2.4
    collecting ... collected 3 items
    
    test_some_are_slow.py ...
    
    ========================= slowest 3 test durations =========================
    0.20s call     test_some_are_slow.py::test_funcslow2
    0.10s call     test_some_are_slow.py::test_funcslow1
    0.00s setup    test_some_are_slow.py::test_funcslow2
    ========================= 3 passed in 0.31 seconds =========================
