<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>テストのアサーションにおける書き込みとレポート</title> <link rel="stylesheet" href="_static/sphinxdoc.css" type="text/css" /> <link rel="stylesheet" href="_static/pygments.css" type="text/css" /> <script type="text/javascript"> var DOCUMENTATION_OPTIONS = { URL_ROOT: '', VERSION: '2.2.4.0', COLLAPSE_INDEX: false, FILE_SUFFIX: '.html', HAS_SOURCE: true }; </script> <script type="text/javascript" src="_static/jquery.js"></script> <script type="text/javascript" src="_static/underscore.js"></script> <script type="text/javascript" src="_static/doctools.js"></script> <script type="text/javascript" src="_static/translations.js"></script> <link rel="top" title="None" href="index.html" /> <link rel="up" title="py.test リファレンスドキュメント" href="apiref.html" /> <link rel="next" title="テスト関数 (funcargs) にオブジェクトを注入" href="funcargs.html" /> <link rel="prev" title="基本的なテストの設定" href="customize.html" /> </head> <body> <div class="related"> <h3>ナビゲーション</h3> <ul> <li class="right" style="margin-right: 10px"> <a href="py-modindex.html" title="Pythonモジュール索引" >モジュール</a></li> <li class="right" > <a href="funcargs.html" title="テスト関数 (funcargs) にオブジェクトを注入" accesskey="N">次へ</a> |</li> <li class="right" > <a href="customize.html" title="基本的なテストの設定" accesskey="P">前へ</a> |</li> <li><a href="contents.html">pytest-2.2.4.0</a> »</li> <li><a href="apiref.html" accesskey="U">py.test リファレンスドキュメント</a> »</li> </ul> </div> <div class="sphinxsidebar"> <div class="sphinxsidebarwrapper"> <h3><a href="contents.html">目次</a></h3> <ul> <li><a class="reference internal" href="#">テストのアサーションにおける書き込みとレポート</a><ul> <li><a class="reference internal" href="#assert"><tt class="docutils literal"><span class="pre">assert</span></tt> 文によるアサーション</a></li> <li><a class="reference internal" href="#id2">例外発生を期待するアサーション</a></li> <li><a class="reference internal" href="#newreport">コンテキストに依存した内容の比較</a></li> <li><a class="reference internal" href="#id4">アサーション比較の定義</a></li> <li><a class="reference internal" href="#assert-introspection">高度なアサートイントロスペクション</a></li> </ul> </li> </ul> <h4>前のトピックへ</h4> <p class="topless"><a href="customize.html" title="前の章へ">基本的なテストの設定</a></p> <h4>次のトピックへ</h4> <p class="topless"><a href="funcargs.html" title="次の章へ">テスト関数 (funcargs) にオブジェクトを注入</a></p> <div id="searchbox" style="display: none"> <h3>クイック検索</h3> <form class="search" action="search.html" method="get"> <input type="text" name="q" /> <input type="submit" value="検索" /> <input type="hidden" name="check_keywords" value="yes" /> <input type="hidden" name="area" value="default" /> </form> <p class="searchtip" style="font-size: 90%"> モジュール、クラス、または関数名を入力してください </p> </div> <script type="text/javascript">$('#searchbox').show(0);</script> </div> </div> <div class="document"> <div class="documentwrapper"> <div class="bodywrapper"> <div class="body"> <div class="section" id="id1"> <h1>テストのアサーションにおける書き込みとレポート<a class="headerlink" href="#id1" title="このヘッドラインへのパーマリンク">¶</a></h1> <div class="section" id="assert"> <span id="assert-with-the-assert-statement"></span><h2><tt class="docutils literal"><span class="pre">assert</span></tt> 文によるアサーション<a class="headerlink" href="#assert" title="このヘッドラインへのパーマリンク">¶</a></h2> <p><tt class="docutils literal"><span class="pre">py.test</span></tt> は、テストで期待値と実際の値を検証するのに Python 標準の <tt class="docutils literal"><span class="pre">assert</span></tt> 文が使えます。例えば、次のようにテストを作成します:</p> <div class="highlight-python"><div class="highlight"><pre><span class="c"># test_assert1.py の内容</span> <span class="k">def</span> <span class="nf">f</span><span class="p">():</span> <span class="k">return</span> <span class="mi">3</span> <span class="k">def</span> <span class="nf">test_function</span><span class="p">():</span> <span class="k">assert</span> <span class="n">f</span><span class="p">()</span> <span class="o">==</span> <span class="mi">4</span> </pre></div> </div> <p>このサンプルは、関数が特定の値を返すのをアサートします。このアサーションが失敗した場合、関数呼び出しの返り値が表示されます:</p> <div class="highlight-python"><pre>$ py.test test_assert1.py =========================== test session starts ============================ platform linux2 -- Python 2.7.1 -- pytest-2.2.4 collecting ... collected 1 items test_assert1.py F ================================= FAILURES ================================= ______________________________ test_function _______________________________ def test_function(): > assert f() == 4 E assert 3 == 4 E + where 3 = f() test_assert1.py:5: AssertionError ========================= 1 failed in 0.01 seconds =========================</pre> </div> <p>py.test は、関数呼び出し、属性、比較、バイナリや単項演算子といった処理を含む通常の部分式の値を表示する機能があります (<a class="reference internal" href="example/reportingdemo.html#tbreportdemo"><em>py.test によるテスト失敗時のレポートのデモ</em></a> を参照) 。この機能により、定型的なコードを必要とせず、Python イディオム的な概念も利用できます。その上でイントロスペクション情報を失うこともありません。</p> <p>但し、次のようにアサーションと一緒にメッセージを指定した場合:</p> <div class="highlight-python"><div class="highlight"><pre><span class="k">assert</span> <span class="n">a</span> <span class="o">%</span> <span class="mi">2</span> <span class="o">==</span> <span class="mi">0</span><span class="p">,</span> <span class="s">"value was odd, should be even"</span> </pre></div> </div> <p>そこでアサートイントロスペクションを行わず、このメッセージは単純にトレースバックで表示されます。</p> <p>アサートイントロスペクションの詳細については <a class="reference internal" href="#assert-details"><em>高度なアサートイントロスペクション</em></a> を参照してください。</p> </div> <div class="section" id="id2"> <h2>例外発生を期待するアサーション<a class="headerlink" href="#id2" title="このヘッドラインへのパーマリンク">¶</a></h2> <p>発生した例外のアサーションを行うには、次のようにコンテキスト マネージャーとして <tt class="docutils literal"><span class="pre">pytest.raises</span></tt> を使います:</p> <div class="highlight-python"><div class="highlight"><pre><span class="kn">import</span> <span class="nn">pytest</span> <span class="k">with</span> <span class="n">pytest</span><span class="o">.</span><span class="n">raises</span><span class="p">(</span><span class="ne">ZeroDivisionError</span><span class="p">):</span> <span class="mi">1</span> <span class="o">/</span> <span class="mi">0</span> </pre></div> </div> <p>もし実際の例外の情報を調べる必要があるなら、次のように行います:</p> <div class="highlight-python"><div class="highlight"><pre><span class="k">with</span> <span class="n">pytest</span><span class="o">.</span><span class="n">raises</span><span class="p">(</span><span class="ne">RuntimeError</span><span class="p">)</span> <span class="k">as</span> <span class="n">excinfo</span><span class="p">:</span> <span class="k">def</span> <span class="nf">f</span><span class="p">():</span> <span class="n">f</span><span class="p">()</span> <span class="n">f</span><span class="p">()</span> <span class="c"># excinfo.type, excinfo.value, excinfo.traceback といった関連する値を確認する</span> </pre></div> </div> <p>Python 2.4 でも同じように動作するテストコードを書きたいなら、例外発生を期待するテストを行う別の方法が2つあります:</p> <div class="highlight-python"><div class="highlight"><pre><span class="n">pytest</span><span class="o">.</span><span class="n">raises</span><span class="p">(</span><span class="n">ExpectedException</span><span class="p">,</span> <span class="n">func</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span> <span class="n">pytest</span><span class="o">.</span><span class="n">raises</span><span class="p">(</span><span class="n">ExpectedException</span><span class="p">,</span> <span class="s">"func(*args, **kwargs)"</span><span class="p">)</span> </pre></div> </div> <p>両方とも指定した関数へ args と kwargs を渡して実行し、引数として与えた <tt class="docutils literal"><span class="pre">ExpectedException</span></tt> が発生することをアサートします。このレポートは <em>no exception</em> または <em>wrong exception</em> といったテストに失敗したときに分かりやすい内容を表示します。</p> </div> <div class="section" id="newreport"> <span id="id3"></span><h2>コンテキストに依存した内容の比較<a class="headerlink" href="#newreport" title="このヘッドラインへのパーマリンク">¶</a></h2> <p class="versionadded"> <span class="versionmodified">バージョン 2.0 で追加.</span></p> <p>py.test は、比較するときにコンテキスト依存の情報を分かりやすく表示します。例えば、:</p> <div class="highlight-python"><div class="highlight"><pre><span class="c"># test_assert2.py の内容</span> <span class="k">def</span> <span class="nf">test_set_comparison</span><span class="p">():</span> <span class="n">set1</span> <span class="o">=</span> <span class="nb">set</span><span class="p">(</span><span class="s">"1308"</span><span class="p">)</span> <span class="n">set2</span> <span class="o">=</span> <span class="nb">set</span><span class="p">(</span><span class="s">"8035"</span><span class="p">)</span> <span class="k">assert</span> <span class="n">set1</span> <span class="o">==</span> <span class="n">set2</span> </pre></div> </div> <p>このモジュールを実行すると:</p> <div class="highlight-python"><pre>$ py.test test_assert2.py =========================== test session starts ============================ platform linux2 -- Python 2.7.1 -- pytest-2.2.4 collecting ... collected 1 items test_assert2.py F ================================= FAILURES ================================= ___________________________ test_set_comparison ____________________________ def test_set_comparison(): set1 = set("1308") set2 = set("8035") > assert set1 == set2 E assert set(['0', '1', '3', '8']) == set(['0', '3', '5', '8']) E Extra items in the left set: E '1' E Extra items in the right set: E '5' test_assert2.py:5: AssertionError ========================= 1 failed in 0.01 seconds =========================</pre> </div> <p>複数のケースにおいて、特別な比較が行われます:</p> <ul class="simple"> <li>長い文字列の比較: コンテキスト diff を表示</li> <li>長いシーケンスの比較: 最初に失敗したインデックス</li> <li>ディクショナリの比較: 異なるエントリ</li> </ul> <p>より多くのサンプルについては <a class="reference internal" href="example/reportingdemo.html#tbreportdemo"><em>レポートのデモ</em></a> 参照してください。</p> </div> <div class="section" id="id4"> <h2>アサーション比較の定義<a class="headerlink" href="#id4" title="このヘッドラインへのパーマリンク">¶</a></h2> <p><tt class="docutils literal"><span class="pre">pytest_assertrepr_compare</span></tt> フックを実装することで独自の詳細説明を追加できます。</p> <dl class="function"> <dt id="_pytest.hookspec.pytest_assertrepr_compare"> <tt class="descclassname">_pytest.hookspec.</tt><tt class="descname">pytest_assertrepr_compare</tt><big>(</big><em>config</em>, <em>op</em>, <em>left</em>, <em>right</em><big>)</big><a class="reference internal" href="_modules/_pytest/hookspec.html#pytest_assertrepr_compare"><span class="viewcode-link">[ソース]</span></a><a class="headerlink" href="#_pytest.hookspec.pytest_assertrepr_compare" title="この定義へのパーマリンク">¶</a></dt> <dd><p>return explanation for comparisons in failing assert expressions.</p> <p>Return None for no custom explanation, otherwise return a list of strings. The strings will be joined by newlines but any newlines <em>in</em> a string will be escaped. Note that all but the first line will be indented sligthly, the intention is for the first line to be a summary.</p> </dd></dl> <p>例として、conftest.py に次のフックを追加してみます。これは <tt class="docutils literal"><span class="pre">Foo</span></tt> オブジェクトの別の説明を提供します:</p> <div class="highlight-python"><div class="highlight"><pre><span class="c"># conftest.py の内容</span> <span class="kn">from</span> <span class="nn">test_foocompare</span> <span class="kn">import</span> <span class="n">Foo</span> <span class="k">def</span> <span class="nf">pytest_assertrepr_compare</span><span class="p">(</span><span class="n">op</span><span class="p">,</span> <span class="n">left</span><span class="p">,</span> <span class="n">right</span><span class="p">):</span> <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">left</span><span class="p">,</span> <span class="n">Foo</span><span class="p">)</span> <span class="ow">and</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">right</span><span class="p">,</span> <span class="n">Foo</span><span class="p">)</span> <span class="ow">and</span> <span class="n">op</span> <span class="o">==</span> <span class="s">"=="</span><span class="p">:</span> <span class="k">return</span> <span class="p">[</span><span class="s">'Comparing Foo instances:'</span><span class="p">,</span> <span class="s">' vals: </span><span class="si">%s</span><span class="s"> != </span><span class="si">%s</span><span class="s">'</span> <span class="o">%</span> <span class="p">(</span><span class="n">left</span><span class="o">.</span><span class="n">val</span><span class="p">,</span> <span class="n">right</span><span class="o">.</span><span class="n">val</span><span class="p">)]</span> </pre></div> </div> <p>ここで次のテストモジュールがあります:</p> <div class="highlight-python"><div class="highlight"><pre><span class="c"># test_foocompare.py の内容</span> <span class="k">class</span> <span class="nc">Foo</span><span class="p">:</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">val</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">val</span> <span class="o">=</span> <span class="n">val</span> <span class="k">def</span> <span class="nf">test_compare</span><span class="p">():</span> <span class="n">f1</span> <span class="o">=</span> <span class="n">Foo</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="n">f2</span> <span class="o">=</span> <span class="n">Foo</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="k">assert</span> <span class="n">f1</span> <span class="o">==</span> <span class="n">f2</span> </pre></div> </div> <p>このテストモジュールを実行すると、conftest ファイルで定義した独自の出力内容が表示されます:</p> <div class="highlight-python"><pre>$ py.test -q test_foocompare.py collecting ... collected 1 items F ================================= FAILURES ================================= _______________________________ test_compare _______________________________ def test_compare(): f1 = Foo(1) f2 = Foo(2) > assert f1 == f2 E assert Comparing Foo instances: E vals: 1 != 2 test_foocompare.py:8: AssertionError 1 failed in 0.01 seconds</pre> </div> </div> <div class="section" id="assert-introspection"> <span id="assert-details"></span><span id="id5"></span><h2>高度なアサートイントロスペクション<a class="headerlink" href="#assert-introspection" title="このヘッドラインへのパーマリンク">¶</a></h2> <p class="versionadded"> <span class="versionmodified">バージョン 2.1 で追加.</span></p> <p>失敗するアサーションに関する詳細のレポートは、実行前に assert 文を書き換えるか、または assert 式を再評価して中間値を記録するかのどちらかの方法で行われます。どちらの方法を使うかは assert の位置、pytest の設定、pytest を実行するのに使われる Python バージョンに依存します。 <tt class="docutils literal"><span class="pre">assert</span> <span class="pre">expr,</span> <span class="pre">message</span></tt> のように直接コード内でメッセージを記述した assert 文は、アサートイントロスペクションが行われず、指定したメッセージがトレースバックに表示されることに注意してください。</p> <p>デフォルトでは、Python バージョンが 2.6 以上の場合、py.test はテストモジュールの assert 文を書き換えます。書き換えられた assert 文は、イントロスペクション情報をアサーションの失敗メッセージに追加します。py.test は、テストコレクション処理で検出したテストモジュールのみを直接書き換えます。そのため、テストモジュールではないサポートライブラリの assert 文は書き換えられません。</p> <div class="admonition note"> <p class="first admonition-title">ノート</p> <p class="last">py.test は、インポート時にテストモジュールを書き換えます。新たに pyc ファイルを書き込むためにインポートフックを使うことでこの処理を行います。この処理はほとんど透過的に行われます。但し、自分でインポートを行ってごちゃごちゃになっている場合、そのインポートフックがインターフェースになる可能性があります。このようなケースでは、単純に <tt class="docutils literal"><span class="pre">--assert=reinterp</span></tt> か <tt class="docutils literal"><span class="pre">--assert=plain</span></tt> を使ってください。さらに、新たに pyc ファイルを書き込めない場合、書き換えはサイレントモードで失敗します。例えば、読み込み専用ファイルシステムや zip ファイルで行うようなときです。</p> </div> <p>assert 文が書き換えられない、または Python バージョン 2.6 よりも小さい場合、py.test はアサーションの再解釈を行います。アサーションの再解釈では、py.test が、assert 文の失敗する部分式を見つけるために assert 文を含む関数のフレームを辿ります。py.test にアサーションの再解釈を行うよう強制するには <tt class="docutils literal"><span class="pre">--assert=reinterp</span></tt> オプションを指定します。</p> <p>アサーションの再解釈は、assert 文の書き換えを行わないことの注意が必要です: それは assert 式の評価が副作用をもつ場合、中間値が安全に決定しないという警告を受け取るかもしれません。この問題の一般的な例として、ファイルを読み込むアサーションがあります:</p> <div class="highlight-python"><div class="highlight"><pre><span class="k">assert</span> <span class="n">f</span><span class="o">.</span><span class="n">read</span><span class="p">()</span> <span class="o">!=</span> <span class="s">'...'</span> </pre></div> </div> <p>このアサーションが失敗した場合、その再評価はおそらく成功します!つまり再評価において2回目に呼び出されたときに <tt class="docutils literal"><span class="pre">f.read()</span></tt> が空の文字列を返すからです。とはいえ、このアサーションを書き換えて、そういったトラブルを避けるのは簡単です:</p> <div class="highlight-python"><div class="highlight"><pre><span class="n">content</span> <span class="o">=</span> <span class="n">f</span><span class="o">.</span><span class="n">read</span><span class="p">()</span> <span class="k">assert</span> <span class="n">content</span> <span class="o">!=</span> <span class="s">'...'</span> </pre></div> </div> <p>全てのアサートイントロスペクションを無効にするには <tt class="docutils literal"><span class="pre">--assert=plain</span></tt> を指定します。</p> <p>詳細については、Benjamin Peterson が詳しくまとめた <a class="reference external" href="http://pybites.blogspot.com/2011/07/behind-scenes-of-pytests-new-assertion.html">Behind the scenes of py.test’s new assertion rewriting</a> を参照してください。</p> <p class="versionadded"> <span class="versionmodified">バージョン 2.1 で追加: </span>代替イントロスペクション手法として assert 書き換え機能を追加</p> <p class="versionchanged"> <span class="versionmodified">バージョン 2.1 で変更: </span><tt class="docutils literal"><span class="pre">--assert</span></tt> オプションを追加。 <tt class="docutils literal"><span class="pre">--no-assert</span></tt> と <tt class="docutils literal"><span class="pre">--nomagic</span></tt> を廃止。</p> </div> </div> </div> </div> </div> <div class="clearer"></div> </div> <div class="related"> <h3>ナビゲーション</h3> <ul> <li class="right" style="margin-right: 10px"> <a href="py-modindex.html" title="Pythonモジュール索引" >モジュール</a></li> <li class="right" > <a href="funcargs.html" title="テスト関数 (funcargs) にオブジェクトを注入" >次へ</a> |</li> <li class="right" > <a href="customize.html" title="基本的なテストの設定" >前へ</a> |</li> <li><a href="contents.html">pytest-2.2.4.0</a> »</li> <li><a href="apiref.html" >py.test リファレンスドキュメント</a> »</li> </ul> </div> <div class="footer"> © Copyright 2011, holger krekel et alii. このドキュメントは <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.1.3 で生成しました。 </div> </body> </html>