Alex Tereshenkov
Alex Tereshenkov

Reputation: 3620

Mock a Python class __init__ partially?

I have a Python class with complicated initialization. I would like to mock the class initialization to avoid writing too much scaffolding code. I want to test its non-mocked method.

A simple example:

class Person:

    def __init__(self, x, y, z):
        self.x = x
        self.y = y
        self.z = z

    def do_work(self):
        return self.x + self.y

There's an answer which shows how to get it done and it works - https://stackoverflow.com/a/21771920/3346915.

Here's a passing test:

from unittest.mock import patch
with patch.object(Person, '__init__', lambda self: None):
    person = Person()
    person.x = 3
    person.y = 4
    assert person.do_work() == 7

I wonder, however, if it would be possible to pass x and y as part of the Person initialization to avoid assigning the fields after the construction to reduce the amount of code?

I wonder if this would be possible?

from unittest.mock import patch
with patch.object(Person, '__init__', lambda self, x, y: None):
    person = Person(x=3, y=4)
    assert person.do_work() == 7

This doesn't work of course because the x and y values are not assigned to the person instance.

Upvotes: 1

Views: 4837

Answers (1)

Daweo
Daweo

Reputation: 36370

lambdas do not support assignment, but you do not have to use lambda as third argument - normal (named) function will work too, so you can do:

class Person:

    def __init__(self, x, y, z):
        self.x = x
        self.y = y
        self.z = z

    def do_work(self):
        return self.x + self.y

from unittest.mock import patch
def newinit(self, x, y):
    self.x = x
    self.y = y
with patch.object(Person, '__init__', newinit):
    person = Person(x=3, y=4)
    assert person.do_work() == 7

(tested in Python 3.7.3)

Upvotes: 3

Related Questions