Python mockito to stub an object for unit testing

eye-catchPython
Sponsored links

mockito is used in the project where I recently joined. I checked the official site but it doesn’t have enough examples to understand the behavior.

So I tried to use it myself to understand how to use it. I hope it helps you too.

Sponsored links

Definition of target functions

Firstly, I defined the following class. These functions’ behavior will be controlled under the test functions.

class ForStub:
    def __init__(self):
        self.__val = 1

    def func1(self, param1: int, param2: int) -> int:
        return param1 + param2

    def func2(self) -> None:
        return

    def func3(self) -> int:
        return self.__val

    def __private_func(self) -> str:
        return "private string value"

    def func4(self) -> str:
        return self.__private_func()
Sponsored links

Using when function

Stub for specific input

If you need to stub the function for a specific input, you can write it this way.

def test1_func1():
    instance = ForStub()
    when(instance).func1(1, 1).thenReturn(3)

    assert 3 == instance.func1(1, 1)
    with pytest.raises(invocation.InvocationError):
        instance.func1(1, 2)

Pass the target instance to when function followed by the function call with the specific arguments you want to stub. After that, you can set any value by thenReturn.

Even though the inputs are (1, 1), the function returns 3. If the arguments are different from the specified ones, it throws InvocationError.

If you need multiple inputs, you can just define it.

def test1_func1():
    instance = ForStub()
    when(instance).func1(1, 1).thenReturn(3)
    when(instance).func1(5, 1).thenReturn(99)

    assert 3 == instance.func1(1, 1)
    assert 99 == instance.func1(5, 1)

    with pytest.raises(invocation.InvocationError):
        instance.func1(1, 2)

Stub for arbitrary input

If you don’t need to specify specific inputs but want to return the dummy value for any input, you can use triple dots instead.

def test2_func1():
    instance = ForStub()
    when(instance).func1(...).thenReturn(3)

    assert 3 == instance.func1(1, 8)
    assert 3 == instance.func1(5, 5)

In this way, func1 always returns the same value for any inputs.

Stub for all instances

The previous way is to stub the function only for the specified instance. If you want to stub a function for all instances that can’t be controlled in the test function, you can pass the class name there instead.

def test3_func1():
    when(ForStub).func1(1, 1).thenReturn(3)

    instance = ForStub()
    assert 3 == instance.func1(1, 1)
    instance2 = ForStub()
    assert 3 == instance2.func1(1, 1)

Using when2 function

If you prefer, you can also use when2 function. The target function needs to be passed for this function.

def test4_func1():
    instance = ForStub()
    when2(instance.func1, ...).thenReturn(3)

    assert 3 == instance.func1(1, 19)
    assert 3 == instance.func1(5, 5)


def test5_func1():
    instance = ForStub()
    when2(instance.func1, 1, 1).thenReturn(3)

    assert 3 == instance.func1(1, 1)
    with pytest.raises(invocation.InvocationError):
        instance.func1(9, 9)

Return None

If you return None, you don’t need to pass any argument to thenReturn.

def test1_func2():
    instance = ForStub()
    when(instance).func2().thenReturn()

    assert None == instance.func2()

Return different result depending on the call count

If it’s necessary to control the return value depending on the call count, you can just add the return value to thenReturn.

def test2_func2():
    instance = ForStub()
    when(instance).func2().thenReturn(1, 2, 3, 4)

    assert 1 == instance.func2()
    assert 2 == instance.func2()
    assert 3 == instance.func2()
    assert 4 == instance.func2()

List value can be set too.

def test3_func2():
    instance = ForStub()
    when(instance).func2().thenReturn([1, 2], (3, 4), [5, 6])

    assert [1, 2] == instance.func2()
    assert (3, 4) == instance.func2()
    assert [5, 6] == instance.func2()

You can also write it in dot chaining.

def test4_func2():
    instance = ForStub()
    when(instance).func2().thenReturn(1).thenReturn(5).thenReturn(8)

    assert 1 == instance.func2()
    assert 5 == instance.func2()
    assert 8 == instance.func2()

Stub private member value

Sometimes, a private member needs to be controlled for various reasons although this is a kind of coding smell.

If you try to change the value in the following ways, it throws Attribute Error.

def test1_func3():
    instance = ForStub()
    with pytest.raises(AttributeError):
        instance.__val == 9876

    with pytest.raises(AttributeError):
        when2(instance.__val).thenReturn(33)

    assert 1 == instance.func3()

The private member can’t be accessed from outside of the class. So we need to change the value in a different way. This is the answer.

def test2_func3():
    instance = ForStub()
    # {'_ForStub__val': 1}
    print(instance.__dict__)
    instance._ForStub__val = 33
    assert 33 == instance.func3()

You can change the value in this way. class_instance._ClassName__private_variable_name.

Stub private member function

Likewise, a private function can be stubbed in the same way.

def test1_func4():
    instance = ForStub()
    # {'_ForStub__val': 1}
    print(instance.__dict__)

    def stub_private_func():
        return "dummy value"

    instance._ForStub__private_func = stub_private_func
    assert "dummy value" == instance.func4()

Comments

Copied title and URL