5

I usually can patch methods of normal objects using pytest monkeypatch. However when I try with a pydantic.BaseModel it fails.

from pydantic import BaseModel


class Person(BaseModel):
    name: str
    age: int

    def intro(self) -> str:
        return f"I am {self.name}, {self.age} years old."


def test_person_intro(monkeypatch):
    p = Person(name='Joe', age=20)
    monkeypatch.setattr(p, 'intro', lambda: 'patched intro')
    assert p.intro() == 'patched intro'

Raises:

monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x10f0d3040>

    def test_intro(monkeypatch):
        p = Person(name='Joe', age=20)
>       monkeypatch.setattr(p, 'intro', lambda: 'patched intro')

- - - - - - - - - - - - - - - - - - - - - 
>   ???
E   ValueError: "Person" object has no field "intro"

pydantic/main.py:422: ValueError

2 Answers 2

5

You could patch the type:

def test_person_intro(monkeypatch):
    p = Person(name='Joe', age=20)
    monkeypatch.setattr(Person, 'intro', lambda self: 'patched intro')
    assert p.intro() == 'patched intro'

Or you could set item in the instance dict:

def test_person_intro(monkeypatch):
    p = Person(name='Joe', age=20)
    monkeypatch.setitem(p.__dict__, 'intro', lambda: 'patched intro')
    assert p.intro() == 'patched intro'

The reason an instance setattr isn't working as usual is that pydantic.BaseModel overrides __setattr__ to only allow a restricted set of names. You will see the same error doing person.intro = "other" as well, it is unrelated to pytest's monkeypatch fixture.

Sign up to request clarification or add additional context in comments.

Comments

2

To simplify from the answer by wim, If wanting to patch manually, this worked for me:

p.__dict__['myattr'] = 'myval'

After this, p.myattr also worked.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.