Praveen
Praveen

Reputation: 31

Creation of Python unit test

function_one.py

class FunctionOne(Base):
    def __init__(self, amount, tax):
        super().__init__(amount, tax)

function_two.py

Class FunctionTwo:
    def __init__(self, a, b, c):
        self.__a = a
        self.__b = b
        self.__c = c

    def _get_info(self):
        x = FunctionOne(0, 1)
        return x

test_function_two.py

class TestPostProcessingStrategyFactory(unittest.TestCase):
    def test__get_info(self):
        a = “a”
        b = “b”
        c = “c”
        amount = 0
        tax = 1

        function_two = FunctionTwo(a, b, c)

        assert function_two.__get_info() == FunctionOne(0,1)

I am trying to create unit test for the function_two.py source code. I get the assertion error that the object at ******** != object at *********. So the two objects address is different. How can make this test pass by correcting the assert statement

assert function_two.__get_info() == FunctionOne(0,1)

Upvotes: 1

Views: 114

Answers (1)

Daniil Fajnberg
Daniil Fajnberg

Reputation: 18693

You need to understand that equality comparisons depend on the __eq__ method of a class. From the code you provided it appears that simply initializing two objects of FunctionOne with the same arguments does not result in two objects that compare as equal. Whatever implementation of __eq__ underlies that class, only you know that.

However, I would argue the approach is faulty to begin with because unit tests, as the name implies, are supposed to isolate your units (i.e. functions typically) as much as possible, which is not what you are doing here.

When you are testing a function f that calls another of your functions g, strictly speaking, the correct approach is mocking g during the test. You need to ensure that you are testing f and only f. This extends to instances of other classes that you wrote, since their methods are also just functions that you wrote.

Have a look at the following example code.py:

class Foo:
    def __init__(self, x, y):
        ...


class Bar:
    def __init__(self, a, b):
        self.__a = a
        self.__b = b

    def get_foo(self):
        foo = Foo(self.__a, self.__b)
        return foo

Say we want to test Bar.get_foo. That method uses our Foo class inside it, instantiating it and returning that instance. We want to ensure that this is what the method does. We don't want to concern ourselves with anything that relates to the implementation of Foo because that is for another test case.

What we need to do is mock that class entirely. Then we substitute some unique object to be returned by calling our mocked Foo and check that we get that object from calling get_foo.

In addition, we want to check that get_foo called the (mocked) Foo constructor with the arguments we expected, i.e. with its __a and __b attributes.

Here is an example test.py:

from unittest import TestCase
from unittest.mock import MagicMock, patch

from . import code


class BarTestCase(TestCase):
    @patch.object(code, "Foo")
    def test_get_foo(self, mock_foo_cls: MagicMock) -> None:
        # Create some random but unique object that should be returned,
        # when the mocked class is called;
        # this object should be the output of `get_bar`:
        mock_foo_cls.return_value = expected_output = object()
        # We remember the arguments to initialize `bar` for later:
        a, b = "spam", "eggs"
        bar = code.Bar(a=a, b=b)
        # Run the method under testing:
        output = bar.get_foo()
        # Check that we get that EXACT object returned:
        self.assertIs(expected_output, output)
        # Ensure that our mocked class was instantiated as expected:
        mock_foo_cls.assert_called_once_with(a, b)

That way we ensure proper isolation from our Foo class during the Bar.get_foo test.

Side note: If we wanted to be super pedantic, we should even isolate our test method from the initialization of Bar, but in this simple example that would be overkill. If your __init__ method does many things aside from just setting some instance attributes, you should definitely mock that during your test as well.

Hope this helps.

References:

Upvotes: 1

Related Questions