のーずいだんぷ

主に自分用メモですが、もしかしたら誰かの役に立つかもしれません

テスト駆動Python読んだ 〜その2:フィクスチャとは?組み込みフィクスチャを使ってみる〜

テスト駆動Pythonをよんだ

タイトルの通り、テストについて本格的に勉強することとした。 最初はちゃんと何ができるかを知るために書籍ベースでやるべきと判断し、唯一のPythonのテスト本である「テスト駆動Python」を読んだ

テスト駆動Python

テスト駆動Python

  • 作者:Brian Okken
  • 出版社/メーカー: 翔泳社
  • 発売日: 2018/08/29
  • メディア: 単行本(ソフトカバー)

本書では組み込み関数のunittestではなく、サードパーティライブラリのpytestを使用して解説している。 一部解釈が曖昧なので2段くらいにしてまとめる。(もしくは今回のやつに追記する。) そして本記事は2回目の記事、もしかしたら4つくらいに分かれるかもしれない。 以下は前回の記事、pytestの基本的なassertionraisesを使ったテストとその他の機能の概要について説明している。

www.nooozui.com

あんまり長いと一覧性がわるいし、トピックも分散するのでキーワードに対する訴求が弱くなって順位が上がらず誰にも読まれない…なんてことになりそうだ。

フィクスチャとは?

テスト関数の実行に先立って、その前後にpytestによって実行される関数のこと と説明されている。言い換えると「テスト専用の関数」みたいなものだろうか。 フレームワークのdjangoで使用されるフィクスチャとは若干意味が異なっているので注意が必要かもしれない。

使い方

@pytest.fixture()をデコレーションするだけ。(使う動詞はデコレーションする、であっているのだろうか…) フィクスチャはクロージャみたいな使い方ができる(というより関数リテラル)

ある値をreturnするようなフィクスチャを定義して、それをテスト関数の引数に渡すとフィクスチャが実行されその値として使用することができる。 具体的には、引数に渡されたフィクスチャをモジュール内部で探索する。 当然だがpytestにおいて命名規則は重要なので注意すること。

フィクスチャってどんなふうに動くの??

例えば以下の様なDBに対して、クエリを投げる処理があるとしてそうなると当然DBのモック(DBの代わりの動作を提供するもの)と、通常DBを扱う際に必要となるコネクションの処理等の初期化処理とクローズするための処理が必要となる。

しかし、テストする関数自体は「クエリを投げるだけ」なので今のままではテストができない。

この場合はフィクスチャのセットアップ処理にDBの初期化処理を記載し、ティアダウン処理にDBをクローズする処理を記載する。

具体的には以下のような感じで記載する。

フィクスチャのyield ってなんだ

yieldはざっくりいうとテストが呼ばれる部分。 つまり初期化処理をyield前に(セットアップ)、終了処理をyield後に書けば良い(ティアダウン)

フィクスチャを他のモジュールで共有する方法

conftest.pyという名前のファイルをpytestではローカルプラグインとして扱う。このconftest.pyの存在するディレクトリ以下に存在するテストはconftest.pyに定義したフィクスチャを共有できる。

フィクスチャ実行をトレースする方法

フィクスチャの挙動を調べるにはpytest --setup-showで結果に表示させることができる。 スクリーンショット 2019-10-29 3 46 58

ERRORとFAILEDの違い

フィクスチャはテストのパラメータとして使用することをできることを説明したが、例えばフィクスチャ内でアサーションをしてその結果が間違った場合と、単純にテストでアサーションエラーが出た場合は出力結果が異なる。 前者の場合はERROR、後者の場合はFAILEDとなる。pytestはここを明確に区別している。

フィクスチャをパラメータとして使用する利点

  1. テストの可読性が良くなる。
  2. パラメータに問題があったときにERRORとなり、テストの失敗なのか判断がつきやすくなる。 また余談として、テストのコメントは、 前提(GIVE)->こうしたら(WHEN)->こうなる(THEN)の形式で記載し、GIVEの部分は可能な限りフィクスチャで用意するほうがよさそう。

  3. フィクスチャのスコープ

  4. function...テスト関数ごとに1回実行。セットアップ、ティアダウンはテスト関数の前後に実施。
  5. class...テストクラスごとに1回実行。
  6. module...モジュールごと、つまり1ファイルごとに1回実行。
  7. session...pytestコマンドごと。セットアップ、ティアダウンは全てコマンド内で共有 なお、フィクスチャが別のフィクスチャに依存する場合、そのスコープは同じかそれ以上に広いものに制限される。(セッションが関数スコープに依存はできないということ、逆は可能)

例えば、上記のフィクスチャを全て作成しテストに使用したところ、次のような結果出力が得られた。

SETUP    S session_scope
    SETUP    M module_scope
        SETUP    F func_scope
        test_scope.py::test_1 (fixtures used: func_scope, module_scope, session_scope).
        TEARDOWN F func_scope
        SETUP    F func_scope
        test_scope.py::test_2 (fixtures used: func_scope, module_scope, session_scope).
        TEARDOWN F func_scope
      SETUP    C class_scope
        test_scope.py::TestSomething::test_3 (fixtures used: class_scope).
        test_scope.py::TestSomething::test_4 (fixtures used: class_scope).
      TEARDOWN C class_scope
    TEARDOWN M module_scope
TEARDOWN S session_scope

間違えやすいが、これらのスコープはフィクスチャを定義した段階で決まる。 テスト関数の呼び出しは無関係である。 - 他のfixutureに依存させてスコープを変更する方法 例えばsessionスコープにしたい→tmpdir また指定するときのデコレータとして、@pytest.mark.usefixtures(<fixture_name>)が使用できる。

  • 常に使用するフィクスチャには@pytest.fixture(autouse=True)を使う 処理結果や例に依存しないテスト(例えばテストにかかった時間とか)を書くと良い ただし、autouseは例外的なものとして使うべきと書いてある

  • フィクスチャ名変更には@pytest.fixture(name=<name>) pytest -setup-show <test_module> でフィクスチャがになっていることが確認できる。 また、--fixtureオプションを指定すると、フィクスチャの場所が取得できる。

最後に…テスト駆動開発について…

ここ最近はテスト駆動開発強化週間として、以下の本も同時進行で読み始めた。

テスト駆動開発

テスト駆動開発

  • 作者:Kent Beck
  • 出版社/メーカー: オーム社
  • 発売日: 2017/10/14
  • メディア: 単行本(ソフトカバー)

また書評アウトプットするが、この本ちょっと直感的でないというか、 全くテスト駆動開発について知らない人がいきなり読むよりも実際のユニットテストがどのようにして行われるか理解してからのほうが良い気がする。