DaenKhaleesi
DaenKhaleesi

Reputation: 189

AttributeError: while using monkeypatch of pytest

src/mainDir/mainFile.py

contents of mainFile.py

import src.tempDir.tempFile as temp

data = 'someData'
def foo(self):
    ans = temp.boo(data)
    return ans

src/tempDir/tempFile.py

def boo(data):

   ans = data
   return ans

Now I want to test foo() from src/tests/test_mainFile.py and I want to mock temp.boo(data) method in foo() method

 import src.mainDir.mainFile as mainFunc

  testData = 'testData'
  def test_foo(monkeypatch):
     monkeypatch.setattr('src.tempDir.tempFile', 'boo', testData)
     ans = mainFunc.foo()
     assert ans == testData

but I get error

AttributeError: 'src.tempDir.tempFile' has no attribute 'boo'

I expect ans = testData.

I would like to know if I am correctly mocking my tempDir.boo() method or I should use pytest's mocker instead of monkeypatch.

Upvotes: 5

Views: 8625

Answers (3)

KyleKing
KyleKing

Reputation: 128

Update: mocking function calls can be done with monkeypatch.setattr('package.main.slow_fun', lambda: False) (see answer and comments in https://stackoverflow.com/a/44666743/3219667) and updated snippet below


I don't think this can be done with pytest's monkeypatch, but you can use the pytest-mock package. Docs: https://github.com/pytest-dev/pytest-mock

Quick example with the two files below:

# package/main.py
def slow_fun():
    return True

def main_fun():
    if slow_fun():
        raise RuntimeError('Slow func returned True')
# tests/test_main.py
from package.main import main_fun

# Make sure to install pytest-mock so that the mocker argument is available
def test_main_fun(mocker):
    mocker.patch('package.main.slow_fun', lambda: False)
    main_fun()

# UPDATE: Alternative with monkeypatch
def test_main_fun_monkeypatch(monkeypatch):
    monkeypatch.setattr('package.main.slow_fun', lambda: False)
    main_fun()

Note: this also works if the functions are in different files

Upvotes: 0

Kyle Finley
Kyle Finley

Reputation: 41

My use case was was slightly different but should still apply. I wanted to patch the value of sys.frozen which is set when running an application bundled by something like Pyinstaller. Otherwise, the attribute does not exist. Looking through the pytest docs, the raising kwarg controls wether or not AttributeError is raised when the attribute does not already exist. (docs)

Usage Example

import sys

def test_frozen_func(monkeypatch):
    monkeypatch.setattr(sys, 'frozen', True, raising=False)
    # can use ('fq_import_path.sys.frozen', ...)
    # if what you are trying to patch is imported in another file
    assert sys.frozen

Upvotes: 4

The Compiler
The Compiler

Reputation: 11929

You're telling monkeypatch to patch the attribute boo of the string object you pass in.

You'll either need to pass in a module like monkeypatch.setattr(tempFile, 'boo', testData), or pass the attribute as a string too (using the two-argument form), like monkeypatch.setattr('src.tempDir.tempFile.boo', testData).

Upvotes: 3

Related Questions