Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Proper way to return mocked object using pytest.fixture

I'm trying to setup the target under test in @pytest.fixture and use it in all my tests in the module. I'm able to patch the test correctly, but after I add the @pytest.fixture to return the mock object and invoke the mocked object in other unit tests the object starting to refer back to the original function.

Following is the code I have. I was expecting the mocked_worker in the unit test to refer to the return value, but it is invoking the actual os.getcwd method instead.

Please help me correct the code:

import os
import pytest
from unittest.mock import patch

class Worker:
    def work_on(self):
        path = os.getcwd()
        print(f'Working on {path}')
        return path

@pytest.fixture()
def mocked_worker():
    with patch('test.test_module.os.getcwd', return_value="Testing"):
        result = Worker()
    return result

def test_work_on(mocked_worker):
    ans = mocked_worker.work_on()
    assert ans == "Testing"
like image 593
Bryan Cheng Avatar asked Nov 26 '19 06:11

Bryan Cheng


People also ask

Does pytest use mock?

Advanced: Mocking in Unit Test In pytest , mocking can replace the return value of a function within a function. This is useful for testing the desired function and replacing the return value of a nested function within that desired function we are testing.

What is Autouse true in pytest?

“Autouse” fixtures are a convenient way to make all tests automatically request them. This can cut out a lot of redundant requests, and can even provide more advanced fixture usage (more on that further down). We can make a fixture an autouse fixture by passing in autouse=True to the fixture's decorator.

Are pytest fixtures cached?

the fixture will be evaluated once per test session and the result will be cached and reused in all dependent tests.

What is the difference between mock and MagicMock?

With Mock you can mock magic methods but you have to define them. MagicMock has "default implementations of most of the magic methods.". If you don't need to test any magic methods, Mock is adequate and doesn't bring a lot of extraneous things into your tests.


Video Answer


2 Answers

The problem is that when the worker returns the scope of "with" statement ends making the object take its real value, the solution is to use "yield".

@pytest.fixture()
def mocked_worker():
    with patch('test.test_module.os.getcwd', return_value="Testing"):
        result = Worker()
        yield result
like image 143
eyllanesc Avatar answered Sep 29 '22 10:09

eyllanesc


I would recommend to use pytest-mock. So full example of one file (test_file.py) solution using this library would be:

import os
import pytest
from unittest.mock import patch

class Worker:
    def work_on(self):
        path = os.getcwd()
        print(f'Working on {path}')
        return path

@pytest.fixture()
def mocked_worker(mocker):  # mocker is pytest-mock fixture
    mocker.patch('test_file.os.getcwd', return_value="Testing")

def test_work_on(mocked_worker):
    worker = Worker()  # here we create instance of Worker, not mock itself!!
    ans = worker.work_on()
    assert ans == "Testing"

used libraries for reference:

pytest==5.3.0
pytest-mock==1.12.1

like image 31
The Hog Avatar answered Sep 29 '22 10:09

The Hog