python3.doc_17
R e i - D r e a m
for Laravel
TOP > Python3 > Python基本(テスト)
Guest
login

最終投稿日:2022年6月18日

Python基本(テスト)
DocTest
● PyCharmでの各○○○.pyファイルのDocTestへの適用
1.[Run]->[Edit Configurations...]
2.ダイアログが立ち上がるので、一番左上にある「+」をクリックする。
3.サブメニューが開くので、[Python tests]->[Doctests]を選択する
4.右側に色々な項目が表示されるので、[Script:]にDocTestを適用したいファイルを指定する。
5.そして、一番上の[Name:]にテスト用の名前を記述する
例)
    doctest_hoge
以上でファイルの適用は完了です。
[Run]->[Run]とすると記述したてすと用の名前があるので、クリックするとテストが開始されます。
【メモ】
複数のファイルをテストするには、上記手順【3.】の後、
4.[Test:]を[All in folder]をチェック
5.[Folder:]にテストしたいディレクトリを選択
6.[Pattern:]にチェックを入れ、テストしたいパターンを記入(例:*.py)
7.[Name:]に適当な名前を登録する
● DocTestとは
DocTestとは、名前の通りドキュメント形式でのテストです。
[モジュール][関数][クラス]を記述する際のドキュメント部分にテストの内容を記述する感じです。
例)
    def payout(money, price):
"""            ※ここから(DocTestは必ずメソッドの最初をコメントから始める必要がある)
支払いをして残高を返します。
手持ちの金額支払いの金額を数値で指定します
>>> payout(1000, 800)                         ※テスト1実行
200                                                               ※テスト1期待値
>>> payout('ほげ', 'ほげ')                    ※テスト2実行
Traceback (most recent call last):    ※テスト2期待値
                                                                       ※実際にエラーになった時の概要の記述を書く
ValueError: money と price は、数値です            ※テスト2期待値
"""                                                                                         ※ここまでがコメントの範囲
if type(money) is not int or type(price) is not int:
    raise ValueError('money と price は、数値です')
if money < price:
    raise ValueError('残金がたりません')
return money - price
テストが終わると、下段のビューあたり(PyCharm)に【Done:2 or 2】的なメッセージが表示されます。
【メモ】
DocTestは、メソッド以外のソースがあればそれも実行しちゃうので、メインのコードではできないのかな。。
【メモ】
関数あたりに DocTsts を記述するとファイル全体が見づらくなるので、テスト専用モジュールを作成するのが良いのかな。。
例)
    [doctest01.py]
"""
支払いをして残高を返します。
手持ちの金額支払いの金額を数値で指定します
>>> from hoge import *                      ※payout メソッドがあるモジュールをインポート
>>> payout(1000, 800)
200
>>> payout(1000, 800)
200
>>> payout('ほげ', 'ほげ')
Traceback (most recent call last):
ValueError: money と price は、数値です
>>> for l in [(1000,900)]:
... payout(l[0], l[1])
100
"""
要するにファイル全部を DocTsts にする。。
● 色々なコマンド類
前述した様にDocTestはコメント内に記述し、テストの実行は【>>> 】と記述し内容を記述する
                     ※半角スペースは必須!
その他にも以下の様なコマンドがあります。
○ テストコードが複数行になる場合
【... 】と記述する
例)
    >>> for i in range(3):                               ※最初は【>>> 】スタート
    ...            print(i)                                          ※次行からは【... 】と表記する
【メモ】
どうも変数の代入はできないっぽい。。
NG例)
    >>> l = [1000, 900]                              ※このリストの代入が SyntaxError と言われる。。。
    ... for l in l:
    ...            print( l[0] )
この場合は、変数の代入もテストとして以下の様に記述する
OK例)
    >>> l = [1000, 900]                             ※これでテストの1つが終了
    >>> for l in l:
    ...            print( l[0] )
○ 期待結果に空白行が存在する場合
例)
import sys
def join_linebreak(word):
"""
>>> join_linebreak([ 'hoge', 'foo', 'bar'] )
hoge
foo
bar
"""
sys.stdout.write('\n'.join(word))
上記のテストは成功します。しかしテスト部分が以下の様になると、期待する結果は以下となります。
>>> join_linebreak([ 'hoge', '', 'bar'] )
hoge
bar
この場合はのテストは失敗します。
理由は空白行があるとテストの終了とみなされるためです。これを回避するには以下の様なタグを記述します。
>>> join_linebreak([ 'hoge', '', 'bar'] )
hoge
<BLANKLINE>                      ※大文字必須!
bar
● Directives オプション
このオプションを指定する事でテストの結果を多少曖昧にしてくれます。
○ NORMALIZE_WHITESPACE
空白スペースを曖昧にしてくれます。
例)
def createList(i):
"""
>>> createList(10)
[1, 2, 3, 4, 5, 6, 7, 8, 9]
"""
print(list(range(1, i)))
上記テストは通ります。が、以下のテストは通りません。
>>> createList(10)
[1, 2, 3, 4, 5, 6, 7, 8, 9]
通らない理由は、スペースが一つ多いからです。
このスペースを曖昧にしたい場合は以下の様にオプションを指定します。
例)
def createList(i):
"""
>>> createList(10) # doctest: +NORMALIZE_WHITESPACE
[1, 2, 3, 4, 5, 6, 7, 8, 9]                                          ※通ります
>>> createList(10) # doctest: +NORMALIZE_WHITESPACE
[1, 2, 3, 4,
5, 6, 7, 8, 9]                                                              ※通ります
>>> createList(10) # doctest: +NORMALIZE_WHITESPACE
[1, 2, 3, 4, 5,6, 7, 8, 9]                                          ※通りません(スペースが無くなるのはダメ!)
"""
print(list(range(1, i)))
○ ELLIPSIS
一部の返却結果を曖昧にしてくれる
例)
from datetime import datetime
def createDteTime():
"""
>>> createDteTime() # doctest: +ELLIPSIS
現在 ... 年 ... 月 ... 日 ... 時 ... 分
"""
d = datetime.now().strftime('現在 %Y 年 %m 月 %d 日 %H 時 %M 分')
print(d)
上記関数を doctest するのは、通常はテストを実行する分単位まで書き直す必要があります。
しかし +ELLIPSIS を使う事で「...」の部分を曖昧に解釈してくれます。
○ SKIP
コードを無視してくれる
例)
>>> a = Hoge # doctest: +SKIP                                ※このコードは無視される
>>> createDteTime() # doctest: +ELLIPSIS
現在 ... 年 ... 月 ... 日 ... 時 ... 分
UnitTest
● PyCharmでの各○○○.pyファイルのUnitTestへの適用
1.[Run]->[Edit Configurations...]
2.ダイアログが立ち上がるので、一番左上にある「+」をクリックする。
3.サブメニューが開くので、[Python tests]->[UnitTest]を選択する
4.右側に色々な項目が表示されるので、[Script:]にDocTestを適用したいファイルを指定する。
5.そして、一番上の[Name:]にテスト用の名前を記述する
例)
    UnitTest_hoge
以上でファイルの適用は完了です。
[Run]->[Run]とすると記述したてすと用の名前があるので、クリックするとテストが開始されます。
【メモ】
複数のファイルをテストするには、上記手順【3.】の後、
4.[Test:]を[All in folder]をチェック
5.[Folder:]にテストしたいディレクトリを選択
6.[Pattern:]にチェックを入れ、テストしたいパターンを記入(例:*.py)
7.[Name:]に適当な名前を登録する
● UnitTestの基本書式
import unittest                                                ※必ずインポートする
class TestSample(unittest.TestCase):    ※任意のクラス名    ※継承は必須
def setUp(self):                                          ※各テストメソッド単位で実行前に呼ばれる
    print('最初に処理する亊...')
def tearDown(self):                                  ※各テストメソッド単位で実行後に呼ばれる
    print('最後に処理する亊...')
def testMyTest01(self):                            ※「test○○○」というメソッド名をテスト対象とする
    self.assertEqual( 4, 2*2, '計算が違うよ。。' )
                     ※「self.assertEqual」は等価テスト ※期待値、処理、NG時のコメント
    self.assertEqual( 5, 2+3, '計算が違うよ。。' )
def testMyTest02(self):
    self.assertEqual( 4, 2*2, '計算が違うよ。。' )
● 色々なテストメソッド
○ 等価テスト
・assertEqual
・assertNotEqual
・assertTrue
・assertFalse
・assertIs
・assertIsNot
・assertIsNone
・assertIsNotNone
・assertIn
・assertNotIn
・assertIsInstance
・assertNotIsInstance
○ 例外や警告テスト
・assertRaises
・assertRaisesRegex
・assertWarns
・assertWarnsRegex
○ その他のテスト
・assertAlmostEqual                      ※浮動小数点の誤差を吸収してくれる
・assertNotAlmostEqual
・assertGreater
・assertGreaterEqual
・assertLess
・assertLessEqual
・assertRegex
・assertNotRegex
・assertCountEqual
・assertLogs
● Mock
Mock とは、あるユニットをテストする際、そのユニットが他のユニットを参照している場合等は、
本来のテスト目的とは違う部分を考慮にいれる必要がある。
しかし Mock を使う事でこれを回避できます。
例)
[hoge.py]
from datetime import datetime
def createDteTime():
    return datetime.now().strftime('%Y年%m月%d日')
[utTest.py]
import unittest
from hoge import *
class TestSample(unittest.TestCase):
    def testMyTest03(self):
        self.assertEqual('2017年10月03日',createDteTime(),'年月日が違うよ。。')
上記テストは、メソッド createDteTime をテストするものだが、テストの目的はフォーマットが「年月日」となっているかどうかです。
しかし、期待値はテストを実行する日で書き換える必要があります。
これを回避するには以下の様に Mock を利用します。
例)
[hoge.py]
from datetime import datetime
def createDteTime():
    return datetime.now().strftime('%Y年%m月%d日')
[utTest.py]
import unittest
from hoge import *
from unittest.mock import Mock, patch      ※[Mock][patch]をインポート
from datetime import datetime                      ※Mock側で日付を操作するので[datetime]もインポート
class TestSample(unittest.TestCase):
@patch('hoge.datetime')                              ※文字列なので注意!
                     ※パッチ宣言。[hoge.py]で使用している[datetime]メソッドをパッチする。
def testMyTest03(self,datetime):
                     ※パッチ宣言したメソッド[datetime]を引数[datetime_mock]で受ける
    datetime.now = Mock(return_value = datetime(2017, 10, 3))
                     ※ここで[datetime_mock]の[now]メソッドには「2017/10/3」としてのオブジェクトしか渡らなくなる
                     ※要するに、[datetime_mock]とは、[hoge.py]でインポートした[datetime]をインターセプトしている。
                     ※そして、[hoge.py]のメソッド[createDteTime]内で処理される[datetime.now()]は、[datetime_mock.now]の内容に差し替わる事になる。
    self.assertEqual('2017年10月03日',createDteTime(),'年月日が違うよ。。')
                    ※なのでこのテストは、常に期待値「2017年10月03日」でも通ります。
ログインしてコメントを残そう!!


レンズモード 【新規購入限定用】
きっぷる