[Python] pytest mocker.patch has finally worked, so I will write down what I found with just momentum

I knew in my head that Python's import introduces names into my namespace. I've finally come to understand it in practice, so I'll write it down so I don't forget it. With great momentum.

file organization

The file structure is like this. Under the ex_mock module are the ExMock class and other classes.

.
├── 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

Test target

The class to be tested. In the constructor, we instantiate the Greeter and Outputter classes.

exmock.py


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)

Test class

This is a test class. We instantiate ExMock and test that the Greeter and Outputter classes are called.

test_exmock.py


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()

What I tried

I tried various ways to write mocker.patch.

NG pattern 1

I made it an actual class name.

outputter = mocker.patch('ex_mock.outputter.Outputter')

The mock was created but failed with assert_called_once ().

NG pattern 2

Then, I made the module name + class name.

outputter = mocker.patch('ex_mock.Outputter')

When creating a mock, I got an error saying that it doesn't exist.

NG pattern 3

I tried only the class name.

outputter = mocker.patch('Outputter')

I got an error saying that the target path name is not correct.

NG pattern 4

I tried to make it a pass.

outputter = mocker.patch('.Outputter')

An error occurred if the module name was empty.

OK pattern

Module name + file name with import + class name.

outputter = mocker.patch('ex_mock.exmock.Outputter')

It went well.

What I found

Python's import means that it introduces a name.

The following import in the test target class introduces the name Outputter to ex_mock.exmock, which is the module name + file name.

exmock.py


from ex_mock.outputter import Outputter

Therefore, in the test class, it will be referred to by the name ex_mock.exmock.Outputter.

What does that mean?

Maybe the way to write mocker.patch changes depending on how to write import?

that's all

Delivered with just momentum. Is it common sense for anyone who knows Python? It's interesting to have a language in which you can play with your own syntax tree from within a program.

Source code

The entire source is on github. I hope it helps.

https://github.com/sengokyu/python-pytest-mock

Recommended Posts

[Python] pytest mocker.patch has finally worked, so I will write down what I found with just momentum
I made a segment tree with python, so I will introduce it
I tried to make a calculator with Tkinter so I will write it
What I did with a Python array
I was able to mock AWS-Batch with python, moto, so I will leave it
I customized it with Visual Studio Code (mainly for python), so I will summarize it