nbgraderのAutograder testsセルにユニットテストを書くだけ。
関数func(x=3)の返り値が1である場合に正解とするなら,以下のように書く。
from nose.tools import eq_
eq_(func(x=3),1)
これを実行したとき,func(x=3)の返り値が不適切なときにエラーが出る。
以下のメソッドのほとんどはエラーが出た時のメッセージmsg="..."を引数に追加できる。
msgを追加できない場合は後述のようにtryとexceptを使うと良い。
| メソッド | |
|---|---|
assert_true(x) |
bool(x) is True |
assert_false(x) |
bool(x) is False |
assert_is(a, b) |
a is b |
assert_is_not(a, b) |
a is not b |
assert_is_none(x) |
x is None |
assert_is_not_none(x) |
x is not None |
assert_is_instance(a, b) |
isinstance(a, b) |
assert_not_is_instance(a, b) |
not isinstance(a, b) |
| メソッド | alias | |
|---|---|---|
assert <expr> |
ok_(<expr>) |
|
assert_equal(a,b) |
eq_(a,b) |
a == b |
assert_not_equal(a, b) |
a != b | |
assert_almost_equal(a,b) |
round(a-b, 7) == 0 | |
assert_greater(a, b) |
a > b | |
assert_greater_equal(a, b) |
a >= b | |
assert_less(a, b) |
a < b | |
assert_less_equal(a, b) |
a <= b |
setの要素には順番が定義されないので,assert_equal()での判定は不適切。
例えば以下の関数がある。
| メソッド | |
|---|---|
assert_in(a, b) |
a in b |
assert_not_in(a, b) |
a not in b |
assert_count_equal(a, b) |
a and b have the same elements in the same number, regardless of their order. |
リスト同士(list, ndarray同士も含む)の比較には,numpy.testingのnt.assert_array_equalを使う。
リスト要素を持つリストが,あるリスト要素を含むか(順不同)を確認したいときには以下のような関数を使うとよい。
# check if src involves tgt
def _def _assert_array_involve(src, tgt, msg=""):
try:
assert any( np.array_equal(tgt,i) for i in src )
except:
_alert(msg)
raise ValueError(msg)
Python unittest - asserting dictionary with listsにのっている以下の方法を使う。dictの要素のtypeによらずに対応できる。
# check if src involves all items in tgt
def _assert_dict_equal(src, tgt, msg=""):
try:
assert all( (k,v) in src.items() for (k,v) in tgt.items() )
except:
_alert(msg)
raise ValueError
| メソッド | |
|---|---|
assert_regex(s, r) |
|
assert_not_regex(s, r) |
以下のように必要なものだけimportして使っている。
from nose.tools import eq_
from nose.tools import ok_
from nose.tools import assert_in
from nose.tools import assert_equal
from nose.tools import assert_not_equal
利用できるメソッドは他にも色々ある。
unittestの関数を概ね使えるみたい。ただし,関数名はassertEqualをassert_equalと,PEP8準拠に変える。assert_array_not_equalなど,notに対する判定ルーチンがない。必要な時にはnumpy.testing.assert_raisesを使う。(情報源)nt.assert_raises(AssertionError, nt.assert_array_equal, L1, L2)
math.isclose(),math.isfinite(x),math.isinf(x), math.isnan(x)isequal()や==よりmath.isclose()の方が有効数字を決めて比較できるので便利(参考)以下は,unittestのpatchを利用して,関数show_listにenumerate()が利用されているかを確認する例。some_argsとsome_args_for_enumerateは,show_list()および,show_list内のenumerate()に渡される引数。
from unittest.mock import patch
with patch('__main__.enumerate') as mock_enumerate:
show_list(some_args)
mock_enumerate.assert_called_once_with(some_args_for_enumerate)
print()の出力を評価するprint文が1回だけ実行される場合
print文`が複数回実行される場合(参考)import sys, io, contextlib
class Data(object):
pass
@contextlib.contextmanager
def capture_stdout():
old = sys.stdout
capturer = io.StringIO()
data = Data()
try:
sys.stdout = capturer
yield data
finally:
sys.stdout = old
data.result = capturer.getvalue()
ここまでが宣言部分。以下が利用方法。
with capture_stdout() as capture:
print("hello") # ここに,監視する関数を列挙する。
print("goodbye")
res=capture.result
ans="hello\n\
goodbye"
assert res.replace(" ","").strip()==ans.replace(" ","").strip() # usually better to remove spaces, and spaces/tabs/\n at the end
関数の返り値が乱数を使って生成される場合,正解か否かの判定は難しいことも多いが,少なくとも乱数で生成されているかどうかの確認は簡単にできる。
result=[rnd_func() for i in range(20)]
result2=[rnd_func() for i in range(20)]
assert_not_equal(result,result2)
さらに,平均値や分散, max, minを使った色々な判定も可能。 ランダムに"stone",“paper”,“scissors"を発生させるような関数なら,上記の確認後に,以下のような確認をするのも良い。
assert "stone" in result
assert "paper" in result
assert "scissors" in result
エラーメッセージは,unittest関数の引数"msg"でも指定できるが, 背景色をつけたり等,メッセージを分かりやすくするにはtry&exceptを使うと便利。
from nose.tools import eq_
try:
eq_(func(x=3),1)
except:
display('func(x=3)の返り値は1でないといけません')
raise
上記のdisplay()で表示される内容を,もっと目立たせるためには,以下のようにエラー
メッセージ表示用の関数を作ると良い。
from IPython.display import HTML
from nose.tools import eq_
def _alert(msg):
display(HTML('<div class="alert alert-danger">{msg}</div>'.format(msg=msg)))
try:
eq_(func(x=3),1)
except:
_alert('func(x=3)の返り値は1でないといけません')
raise
採点対象になっている関数が,その後のセルの評価に影響するときは要注意。評価時には受講生の内容に全て置き換わるので,受講生に不適切に定義された関数がその後の評価を不適切なものにしてしまうことがある。
<div class="alert alert-success">緑色<div class="alert alert-info"> 青色<div class="alert alert-warning"> 黄色<div class="alert alert-danger"> 赤色raise SystemExit("Stop right here!")