Pythonのimport
は、自名前空間に名前を導入するものだと、頭ではわかっていました。
それがやっと実地でもわかるようになりましたので、忘れないように書いておきます。すごい勢いで。
ファイル構成
このようなファイル構成になっています。
ex_mockモジュールの下に、ExMockクラスやその他のクラスがあります。
.
├── README.md
├── ex_mock
│ ├── __init__.py
│ ├── __main__.py
│ ├── creator.py
│ ├── exmock.py
│ ├── greeter.py
│ └── outputter.py
├── poetry.lock
├── pyproject.toml
└── tests
├── __init__.py
├── test_ex_mock.py
├── test_exmock.py
└── test_greeter.py
テスト対象
テスト対象のクラスです。
コンストラクタのなかで、Greeter
クラスとOutputter
クラスをインスタンス化しています。
from ex_mock.outputter import Outputter
from ex_mock.greeter import Greeter
class ExMock:
def __init__(self):
self.__greeter = Greeter('Hello', Outputter())
def exec(self, name):
self.__greeter.greet(name)
テストクラス
テストクラスです。
ExMock
をインスタンス化し、Greeter
クラスとOutputter
クラスが呼ばれていることをテストしています。
from ex_mock.exmock import ExMock
class TestExmock:
def test_init(self, mocker):
# Given
outputter = mocker.patch('ex_mock.exmock.Outputter')
greeter = mocker.patch('ex_mock.exmock.Greeter')
# When
instance = ExMock()
# Then
outputter.assert_called_once()
greeter.assert_called_once()
試したこと
mocker.patch
の書き方を色々試しました。
NGなパターン 1
実際にあるクラス名にしました。
outputter = mocker.patch('ex_mock.outputter.Outputter')
モックは作成されましたが、assert_called_once()
で失敗しました。
NGなパターン 2
それではと、モジュール名+クラス名にしました。
outputter = mocker.patch('ex_mock.Outputter')
モック作成時に、そんなもん存在しないぜとエラーになりました。
NGなパターン 3
クラス名だけにしてみました。
outputter = mocker.patch('Outputter')
ターゲットパス名がよろしくないぜとエラーになりました。
NGなパターン 4
パスにしてみました。
outputter = mocker.patch('.Outputter')
モジュール名が空だとエラーになりました。
OKなパターン
モジュール名 + importが書いてあるファイル名 + クラス名にしました。
outputter = mocker.patch('ex_mock.exmock.Outputter')
うまくいきました。
わかったこと
Pythonのimport
は、名前を導入するものだということです。
テスト対象クラスにある下記import
は、モジュール名+ファイル名であるところのex_mock.exmock
にOutputter
という名前を導入しています。
from ex_mock.outputter import Outputter
したがって、テストクラスの中ではex_mock.exmock.Outputter
という名前で参照することになるのです。
ということは?
もしかして、import
の書き方によって、mocker.patch
の書き方も変わる?
以上です
勢いだけでお届けしました。
Pythonを知っている人にとっては、常識的な話なんでしょうか?
プログラムの中から自分自身の構文木をいじれる言語っておもしろいですね。
ソースコード
ソース全体は github に置いてあります。何かの参考になれば。