2

I've been trying for days to mock the instance of a certain class. I've tried dozens of approaches... I'm starting to think that this is not possible.

The code below is the only one, so far, that I've tested that can patch the SomeClassToMock class (note that I need to use __new__ to work). However the error TypeError: object.__new__() takes exactly one argument (the type to instantiate) occurs when "test_some_method_b" is called... If I just run "test_some_method_b" or "test_some_method_a", the error doesn't occur (weird? )!

I don't know if there's something wrong, but I really need to instantiate (mock) SomeClassToMock in a controlled way.

Is there any possible solution?

CODE

import unittest
from unittest import mock
from some.path import SomeClassToMock
from path.to.module_to_test import ClassToTest


class SomeClassTest(unittest.TestCase):

    def setUp(self):
        pass

    def tearDown(self):
        pass

    def test_some_method_a(self):
        mocked_op = SomeClassToMock(**{
            "some_key_a": 770675,
            "some_key_b": "BLAHBLAHBLAHBLAH",
            "some_key_c": 2740,
            "some_key_d": 343.4,
            "some_key_e": "09/06/2022",
            "some_key_f": "1170720100038737"
        })
        patcher = mock.patch("some.path.SomeClassToMock.__new__", return_value=mocked_op)
        patcher.start()
        effective_op = ClassToTest().method_to_test(
            "value_a",
            "value_b",
            "value_c",
            "value_d"
        )
        patcher.stop()
        self.assertEqual(mocked_op, effective_op)


    def test_some_method_b(self):
        mocked_op = SomeClassToMock(**{
            "some_key_a": 770675,
            "some_key_b": "BLAHBLAHBLAHBLAH",
            "some_key_c": 2740,
            "some_key_d": 343.4,
            "some_key_e": "09/06/2022",
            "some_key_f": ""
        })
        patcher = mock.patch("some.path.SomeClassToMock.__new__", return_value=mocked_op)
        patcher.start()
        effective_op = ClassToTest().method_to_test(
            "value_a",
            "value_b",
            "value_c",
            "value_d"
        )
        patcher.stop()
        self.assertEqual(mocked_op, effective_op)

PYTEST OUTPUT

(project_name) [username@username-pc project_name]$ pytest -v /home/username/tests/certain/path/file_test.py
================================================================= test session starts =================================================================
platform linux -- Python 3.8.12, pytest-5.4.2, py-1.8.1, pluggy-0.13.1 -- /home/username/.pyenv/versions/3.8.12/envs/project_name/bin/python3.8
cachedir: .pytest_cache
rootdir: /home/username/tests
plugins: forked-1.1.3, xdist-1.32.0, cov-2.8.1
collected 2 items                                                                                                                                     

certain/path/file_test.py::SomeClassTest::test_some_method_a PASSED                     [ 50%]
certain/path/file_test.py::SomeClassTest::test_some_method_b FAILED                   [100%]

====================================================================== FAILURES =======================================================================
___________________________________________________ SomeClassTest.test_some_method_b ___________________________________________________

self = <certain.path.file_test.SomeClassTest testMethod=test_some_method_b>

    def test_some_method_b(self):
>       mocked_op = SomeClassToMock(**{
            "some_key_a": 770675,
            "some_key_b": "BLAHBLAHBLAHBLAH",
            "some_key_c": 2740,
            "some_key_d": 343.4,
            "some_key_e": "09/06/2022",
            "some_key_f": ""
        })
E       TypeError: object.__new__() takes exactly one argument (the type to instantiate)

certain/path/file_test.py:37: TypeError
=============================================================== short test summary info ===============================================================
FAILED certain/path/file_test.py::SomeClassTest::test_some_method_b - TypeError: object.__...
============================================================= 1 failed, 1 passed in 1.57s =============================================================

Thanks!

Eduardo Lucio
  • 1,771
  • 2
  • 25
  • 43
  • 1
    I don't think what you want is impossible, but at first glance it seems like `mock.patch` on a `SomeClass.__new__` is not being reset back after calling `.stop()`... Without knowing how you defined the class, maybe [look at the comments on this thread](https://stackoverflow.com/questions/64737213/how-to-patch-the-new-method-of-a-class) and see if there are relevant information there as the OP there has a similar issue to what you are facing. – metatoaster Jun 21 '22 at 01:18
  • Yes, that's what happens, in my opinion. If there is a way around this problem, for me that would be an acceptable solution. – Eduardo Lucio Jun 21 '22 at 01:21
  • 1
    Try out what the OP did on the update and see if that serves as an workaround, but IMO this is a bug with `mock.patch`. – metatoaster Jun 21 '22 at 01:23
  • @metatoaster For the approach you pointed out to work I apparently need to modify the class I'm testing. So in that case this solution would not work for me. Thanks! – Eduardo Lucio Jun 21 '22 at 01:29
  • 1
    This is definitely [an open bug in Python](https://bugs.python.org/issue25731). I can confirm that this behavior would work as expected in Python 2.7 but definitely is broken from Python 3.3 onwards. Python 3.2 seem to also work as expected. – metatoaster Jun 21 '22 at 02:06
  • 1
    [This gist](https://gist.github.com/metatoaster/c2d6757963df13f7e5584f36a4e9f49f) provides the minimum complete reproducible example for this behavior. – metatoaster Jun 21 '22 at 02:14

0 Answers0