質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

アルゴリズム

アルゴリズムとは、定められた目的を達成するために、プログラムの理論的な動作を定義するものです。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

2回答

5998閲覧

pythonで無限ループに入ることをunittestしたい

siruku6

総合スコア1382

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

アルゴリズム

アルゴリズムとは、定められた目的を達成するために、プログラムの理論的な動作を定義するものです。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2020/02/08 19:59

編集2020/02/09 02:13

方針

input関数を使ってコンソール上でユーザーの入力を促す処理を書いています。

この処理をwhileループ内に書き、条件を満たすまではループから抜けさせないようにしています。

この処理に対してunittestを書こうと思っています。

問題

input関数に対してmockを利用して、

  • 無限ループに陥る入力値
  • ループを抜けられる入力値

をそれぞれ入力させてテストしたいのですが、無限ループに陥る入力値を与えた場合、whileループを抜けられずにテストコードが実行終了しないのではないかと思います。

やりたいこと

ある入力値を与えると無限ループに陥ることをテストしたい場合はどのようにコードを書いたら良いのでしょうか?

ご存知の方がいらっしゃれば教えていただければ幸いです...。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

cateye

2020/02/09 00:48 編集

>入力値を与えると無限ループに陥ることをテスト・・・これって、(規模にもよるけど…)机上デバッグじゃダメなんですか?
siruku6

2020/02/09 00:58 編集

真面目に考えて下さっているところ申し訳ないのですが、正直言えば机上デバッグで問題ありません。 今回はunittestの勉強もかねて質問しているので、実現方法がないということがわかればそれでもよいかなと思っています。 ただ、可能ならpip install できるモジュールにしたいと思っていたので、全メソッドをテストしたいという野望はやはりあります!
dodox86

2020/02/09 02:04

pythonに限らず「無限ループ」とはプログラム(≒プログラマー)から見て無限に回り続ける(であろう)現象でしかないので、そのこと自体を評価する方法は無い気がします。脱出条件を満たさない限りループ中のあるステップを必ず通ることを判定するか、脱出条件を満たしたらそのステップを通らずにループ外のステップを通ったことを判定するかしかない気がしますがどうでしょう。ただ、評価対象としている無限ループの処理の実行中は外から入力値を与えるのは(スレッドなどを使えば不可能では無いものの)難しいですね。
siruku6

2020/02/09 02:10

dodox86さんのコメントを見て、考えたことがあったので書いてみます。 世の中には、正しい入力が与えられるまで再度入力を促すシステムはそこそこあると思うのですが、無限ループを使わずに再入力を促さないと、こういった処理を自動テストするのは難しいのかもしれないですね。 いったん、無限ループではなくて再帰処理を使うなどの方法で書き直すことも考えてみます。
dodox86

2020/02/09 02:17

丁度、shiracamusさんが回答されましたが、私も過去のpython以外のプロジェクトでは「無限ループ自体」を評価するのではなく、ずっと実行し続けることを示す状態変数の結果を判定するようなコードにし、無限ループ内の処理はいわばひとつの関数にして対応してました。複雑な条件で実行し続ける場合は別スレッドから入力値を与るなどして状態変数の遷移をリスト化し、単体テストの評価とするようなかたちにしていましたね。(参考として)
siruku6

2020/02/09 02:24 編集

丁寧にありがとうございます!! コストに見合うメリットが得られるのか非常に怪しく感じられてしまいました...ただ、実務で実装されているということであれば、やはりできるようになっておく価値はあるのかな、と感じました。 私も普段の仕事では python はあまり使わないのですが、自動テスト自体は書くことがあるので、そちらにも生かせるように頑張ってみます^^
guest

回答2

0

input_data.py と その テストをかいてみました。

input_data.py

python3

1class input_data: 2 def get_number(self): 3 while True: 4 try: 5 n = int(input("1 以上の数字をいれてください\n")) 6 if n > 0: 7 return n 8 except Exception as e: 9 pass 10 11if __name__ == '__main__': 12 n = input_data().get_number() 13 print(n)

test_input_data.py

python3

1import unittest.mock 2import unittest 3 4from input_data import input_data 5 6class TestStringMethods(unittest.TestCase): 7 def test_no_retry(self): 8 with unittest.mock.patch('builtins.input', side_effect=['1']): 9 self.assertEqual(input_data().get_number(), 1) 10 11 12 def test_retry(self): 13 with unittest.mock.patch('builtins.input', side_effect=['a', '', '0', '-1', 'e3\n', '1']): 14 self.assertEqual(input_data().get_number(), 1) 15 16 17if __name__ == '__main__': 18 unittest.main()

無限ループに陥る入力値
ループを抜けられる入力値

でなく、

ループを抜けない入力値
ループを抜けられる入力値

をテストしています。

実行例
イメージ説明

参考情報:

  • how to use builtins.input for multiple inputs

https://stackoverflow.com/questions/55580016/

投稿2020/02/09 03:37

katoy

総合スコア22324

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

siruku6

2020/02/09 03:46

side_effect で、inputの戻り値を順番に変更し、最後の1まできたらreturnに到達することができて、処理が終了する、ということでしょうか? コード自体も複雑ではなく可読性も高いので非常に良いものだと感じました! 今回はスピード回答していただいたshiracamusさんにベストアンサーをつけさせていただきますが、非常にわかりやすいコードを掲載して下さったkatoyさんにも、高評価1ポチさせていただきます。
katoy

2020/02/09 05:12

> side_effect で、inputの戻り値を順番に .. はい、テスト入力の最後にループが終了する値を配置し、その値が返ってくることで 入力ループが内部で回っていることを確認するようにしています。 stdput の内容もチェックするおうにすれば、"入力してください" のメッセージが繰り返し表示されることなどもチェックできるはずです。
siruku6

2020/02/09 12:53

メッセージまで確認できるんですね!!!! いろいろとありがとうございます・・・。
katoy

2020/02/10 23:06

"python unittest stdout" などで検索すると、情報を得られると思います。
guest

0

ベストアンサー

テスト対象はテスト可能であることが条件なので、「無限ループに入った」ことを判断できるようにして、処理を終了できる必要があります。
例えば、無限ループ処理部分を関数化してもらい、テスト時はその関数を例外発生させるmockに置き換えて、例外発生すればテスト成功ということにできます。
無限ループするときの while True: のような処理部分を while loop(): のようにして普段は return True するだけのloop関数にしておいて、テスト時にloop関数を例外発生させるmockに置き換えてもいいと思います。
あとは、unittest側で実行時間制限できるなら、10秒以内に処理が終了しなければ成功判定するなどの手もあると思います。

投稿2020/02/09 01:59

編集2020/02/09 02:09
shiracamus

総合スコア5406

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

siruku6

2020/02/09 02:04

回答ありがとうございます! 1. 「無限ループに入った」ことを判断できるように 2. unittest側で実行時間制限できるなら、10秒以内に処理が終了しなければ成功判定する のどちらかということで、確かにどちらもできそうな気がしました。 ただ、2のアプローチについては、プロジェクトが大きくなると忌み嫌われると思いますので採用しないつもりです。 (これが積み重なるとテスト実行時間が膨大になる。私以外の人にも決して採用して欲しくないです。)
siruku6

2020/02/09 02:13

pythonというよりも自動テスト、アルゴリズムの問題だと感じたので「アルゴリズム」タグを追加しました....
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問