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

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

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

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

Python

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

Q&A

解決済

1回答

1748閲覧

Pythonのunittestとmockを組み合わせた際のsetUpの使い方

nakamiri

総合スコア7

Python 3.x

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

Python

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

1グッド

2クリップ

投稿2018/04/06 13:42

編集2018/04/09 02:23

前提・実現したいこと

26.6.3.4. Applying the same patch to every test method
上記ドキュメントを参考に、全てのテストメソッドに同じpatchを当てた状態のインスタンスをsetUp内で生成して、
各テストケースで利用するようなテストを書きたいと思っています。

この時にMockオブジェクトに想定のものが上手く反映されず、
どのように使ったらよいか教えてもらえませんでしょうか。

発生している問題・エラーメッセージ

setUp内でpather.start()で返されるMockオブジェクトに対して、
以下のように設定をした場合、patchを当てたクラスの方では想定のMockオブジェクトではなく、
何も設定されていないMockオブジェクトが渡っているようです。
(patchを当てたいのはconfigparser.ConfigParserです)

python

1# test_hoge,py 2from unittest import TestCase, mock 3from libs.hoge import Hoge 4 5class TestHoge(TestCase): 6 7 def setUp(self): 8 pathcer = mock.patch('libs.hoge.ConfigParser') 9 self.addCleanup(pathcer.stop) 10 self.conf_mock = pathcer.start() 11 12 # この辺でMagicMockに諸々の挙動を入れたい 13 self.conf_mock.get = mock.MagicMock(side_effect=self._side_effect) 14 self.conf_mock.getboolean.return_value = False 15 self.conf_mock.getint.return_value = 5 16 17 # ↑で入れたMagicMockを使ってインスタンス化したい 18 self.hoge = Hoge() 19 20 def _side_effect(self, section, option): 21 return "{0}-{1}".format(section, option) 22 23 def test_test(self): 24 a = self.hoge 25 26 # 何かする

python

1# libs/hoge.py 2from configparser import ConfigParser 3from definitions import CONFIG_PATH 4 5class Hoge(object): 6 7 def __init__(self): 8 # このコードで実行するとMagicMockが↑で挙動を入れたものではない、新規のが入ってくるように見える 9 conf = ConfigParser() 10 conf.read(CONFIG_PATH) 11 12 self.host = conf.get('settings', 'host') 13 self.user = conf.get('settings', 'user') 14 self.pwd = conf.get('settings', 'pwd') 15 self.flag = conf.getboolean('settings', 'flag') 16 self.port = conf.getint('settings', 'port') 17 18 def nanka_method(self): 19 # ... 20

試したこと

関数に対してのpatchは上手くできていました。
他に色々パスを変えたりなどして試してみたのですが、全てうまく行かなかった形でした。
やり方問わずテストケース全体にカジュアルにMockを当てた状態の初期化されたインスタンスを使う方法があれば、
そちらのやり方でも構いませんので教えて頂けるとありがたいです。

試したこと1

test_hoge.py のコンストラクタ内で以下のようなコードでのMockへの値追加も試しましたが、変わらずでした

python

1# test_hoge.py:__init__ 2 ConfigParser.get = mock.MagicMock(side_effect=self._side_effect) 3 ConfigParser.getboolean.return_value = False 4 ConfigParser.getint.return_value = 5

試したこと2

関数へのMockは以下のような形でやって上手く行っていました。

python

1# test_hoge.py 2from unittest import TestCase, mock 3from libs.hoge import Hoge 4 5def _side_effect(self, section, option): 6 return "{0}-{1}".format(section, option) 7 8class TestHoge(TestCase): 9 10 @mock.patch('libs.hoge.ConfigParser.get', side_effect=_side_effect) 11 @mock.patch('libs.hoge.ConfigParser.getboolean', return_value=False) 12 @mock.patch('libs.hoge.ConfigParser.getint', return_value=5) 13 def test_test(self): 14 a = Hoge() 15 16 # 何かする

試したこと3

関数へのMockですが、この形だと上手く入りませんでした
初期化のタイミングが異なるとかなのでしょうか...?

python

1# test_hoge.py 2from unittest import TestCase, mock 3from libs.hoge import Hoge 4 5def _side_effect(self, section, option): 6 return "{0}-{1}".format(section, option) 7 8class TestHoge(TestCase): 9 10 @mock.patch('libs.hoge.ConfigParser') 11 def test_test(self, parser): 12 parser.get = mock.MagicMock(side_effect=_side_effect) 13 conf_parser.getboolean.return_value = False 14 conf_parser.getin.return_value = 5 15 16 a = Hoge() 17 18 # 何かする

補足情報(FW/ツールのバージョンなど)

  • PyCharm CE 2016.3
  • Python 3.6.5
  • macOS Sierra 10.12.6
ZENZAI👍を押しています

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

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

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

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

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

guest

回答1

0

ベストアンサー

モック難しいですね。テストケース全体にカジュアルにモックを適用するならテストケースクラス自体をpatch()でデコレートしてあげるとスッキリ書ける気がします。

python

1from configparser import ConfigParser 2from io import StringIO 3from textwrap import dedent 4from unittest import main 5from unittest.mock import Mock 6from unittest.mock import patch 7from unittest import TestCase 8 9 10class ConfigWrapper(object): 11 def __init__(self): 12 conf = ConfigParser() 13 conf.read_file(StringIO(dedent(''' 14 [settings] 15 host = host 16 user = user 17 pwd = pwd 18 '''))) 19 self.host = conf.get('settings', 'host') 20 self.user = conf.get('settings', 'user') 21 self.pwd = conf.get('settings', 'pwd') 22 23 24ConfigMock = Mock() 25instance = ConfigMock() 26instance.get = lambda *args: '-'.join(map(str, args)) 27instance.getboolean.return_value = False 28instance.getint.return_value = 5 29 30 31@patch(__name__ + '.ConfigParser', new=ConfigMock) 32class Test(TestCase): 33 def test_settings(self): 34 config = ConfigWrapper() 35 self.assertEqual(config.host, 'host') 36 37 38if __name__ == '__main__': 39 main()

あとモックを結構作り込むんだったら、普通に自分でモックを書いた方が、モックの使い方に悩むこともなく良いんじゃないかと思いました。

python

1class ConfigMock: 2 def read_file(self, *_, **__): 3 pass 4 5 def get(self, *args): 6 return '-'.join(map(str, args)) 7 8 def getboolean(self, *_, **__): 9 return False 10 11 def getint(self, *_, **__): 12 return 5

投稿2018/04/09 02:12

YouheiSakurai

総合スコア6142

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

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

nakamiri

2018/04/09 02:42

ありがとうございます! `ConfigParser` の箇所はライブラリ側でテストがあるので、 そこ自体の処理をモックでスキップさせたかったので、自分でモックを書くやり方でうまくできました! 全体にモックを書けるならクラス自体にかけたほうが楽なんですね。 併せて教えていただきありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.49%

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

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

質問する

関連した質問