テスト。 開始する


こんにちは この一連の投稿では、Python、特にdjangoプロジェクトでのコードのテストについてお話します。 単体テスト(単体テスト)、静的コード分析、およびWebサイトテストの落とし穴について説明します。

テストの利点に関する導入部分は省略します。テストで覆われたコードは柔らかくシルキーになり、怠zyなものだけがこれについてまだ読み書きしていません。

ユニットテスト


単体テストを実装するための標準モジュール(unittest、以前はpyunit)はPythonバージョン2.1に登場し、Javaを備えたJUnitポートでした(キャメルケースでさえ、 pep8とは反対にメソッドの命名を残していました )。 python 2.7(3.2)で、unittestは多くの新しい興味深いことを追加しました:追加のチェック( assert* )、個別のテストをスキップできるデコレーター( @skip@skipIf )、または開発者が知っている壊れたテストを示す( @expectedFailure )、方法が変更されましたコマンドラインから起動します。 python 2.4以降では、これらの変更のためのunittest2と呼ばれるポートもあります

このことはどのように機能しますか。 非人間的に複雑なモジュールrun_once.pyがあるとします:

 def run_once(f): """ .    . """ def _f(*args, **kwargs): if not hasattr(_f, "_retval"): _f._retval = f(*args, **kwargs) return _f._retval return _f 

(これは、パラメーター関数の最初の呼び出しの結果を保存し、保存された値を常に返すデコレーターです。)

モジュールのテストは次のようになります。

 import unittest class Test(unittest.TestCase): def test_run_once(self): @run_once def inc(n): return n + 1 #     inc()... self.assertEqual(inc(7), 8) # ...  --   self.assertEqual(inc(0), 8) if __name__ == "__main__": unittest.main() 

最後の2行は、このモジュールのすべてのテストを見つけて実行するランチャーを表しています。 これでコンソールで実行できます

 $ python run_once.py 

そして、テスト結果を観察します。

 . --- Ran 1 test in 0.000s OK 

doctest


最近の単体テストでは誰も驚かないので、私はpythonに固有の1つのこと、doctestsを紹介します。 これは説明するよりも表示する方が本当に簡単です。

 def run_once(f): """ >>> @run_once ... def foo(n): return n + 1 >>> foo(7) 8 >>> foo(0) 8 """ def _f(*args, **kwargs): if not hasattr(_f, "_retval"): _f._retval = f(*args, **kwargs) return _f._retval return _f if __name__ == "__main__": import doctest doctest.testmod() 

開始キー:

 $ python run_once2.py -v Trying: @run_once def foo(n): return n + 1 Expecting nothing ok Trying: foo(7) Expecting: 8 ok Trying: foo(0) Expecting: 8 ok 1 items had no tests: __main__ 1 items passed all tests: 3 tests in __main__.run_once 3 tests in 2 items. 3 passed and 0 failed. Test passed. 

関数のdocstringが、開発者とインタープリターの両方に等しく理解できる(希望する)コード例になっていることがわかります。

従来の単体テストと比較して、ドキュメントにはプラス(書きやすさ、インタラクティブなpythonセッションから直接コピーできます;ドキュメントは常にコードに一致します)とマイナス(複雑なコードはすぐに読めなくなります;テキストエディターはそのようなコードを強調表示しませんが、静的アナライザーはエラーを検出しません)。 ただし、小さな例(例のように)にはdockstestを使用し、より複雑なタスクにはユニットテストを使用することを妨げるものはありません。

py.test


pythonの標準パッケージに含まれるツールに加えて、 py.testなどの代替ツールがあります。 インストールは通常通りです

 easy_install -U pytest #  pip install -U pytest 

最初の例の関数を使用します。 変更された単体テストは次のようになります。

 def test_run_once(): @run_once def inc(n): return n + 1 #     inc()... assert inc(7) == 8 # ...  --   assert inc(0) == 8 

行こう:

 $ py.test run_once3.py === test session starts === platform darwin -- Python 2.6.1 -- pytest-2.0.3 collected 1 items run_once3.py . === 1 passed in 0.01 seconds === 

py.testの主な機能(良い):APIはありません(公平に:例外的な場合、APIはまだ必要ですが、非常に小さいです)。 assertを介してチェックします。 これにより、たとえば、計算後に実稼働サーバーにpy.testがインストールされていなくても、テストを実行できる可能性があります(わかりません)。 テストは、クラスとして( test_*のスタイルで)、または単にtest_*形式の関数として発行できます。

ただし、APIがないことには欠点があります。プロジェクトに接続した新しい開発者は、この関数のヒープを起動する方法を理解できない可能性があります。 ただし、開発者はそれほど新しいものではないため 、どのような場合でもテストを実行する方法を文書化するほうが安全です。


noseは、unittest(および--with-doctest )を通じてテストを実行するためのツールです。 また、独自のAPIもありますが、これはオプションです。 上記のすべての例で成功します:

 $ nosetests * -v --with-doctest test_run_once (run_once.Test) ... ok Doctest: run_once2.run_once ... ok run_once3.test_run_once ... ok --- Ran 3 tests in 0.008s OK 

Yur4egが示唆するように(ありがとう!)、Noseはtest_*の形式のファイルからテストを自動的に収集し、testsフォルダーがあればそれを見るのに十分スマートに、 coverage.py (-- --with-coverage )を使用してコードカバレッジを測定できます。 また、最後の実行で落ちたテスト( --failed )のみを実行できます。

私はシムに休暇を取っています。 例のソースコードを添付するためだけに残っています 、3つの部分。 パブリックドメイン。

次号では、通常のDjangoテストツールとその対処方法。

Source: https://habr.com/ru/post/J121162/


All Articles