gruvin
gruvin

Reputation: 51

How to override (not patch) a single method in a python unit test

I've searched for hours. Can't find anyone even trying to do this. Hmmm.

I believe I have to override a single method within a class instance. I do not mean patch(return_value=). I need to make the method in question do something involving self.

I'll try to break it down. Liberally paraphrasing and including one of the many things I tried, which doesn't work ...

class SetupClass(object):
    def set_some_stuff(self):
        data_list = functon_cannot_be_run_on_test_platform()
        self.something = data_list[0]
        self.something_else = data_list[1]

class UUT(object):
    self.config = SetupClass()
    assert self.config.something == 'foo'

class UnitTests(TestCase):

    @patch('SetupClass')
    def test_UUT(self, mock1):

        def WedgeClass(SetupClass):
            def set_some_stuff(self):
                self.something = 'foo'
                pass # I'm a Python newbie, in too deep

        wedge_class = WedgeClass()
        mock1.return_value = wedge_class # doesn't work. context errors

        uut = UUT() # <-- would crash here, because assert above

Assume that I cannot make changes to UUT or SetupClass.

Testing cannot even get off the ground because the assertion will fail, due to, SetupClass.functon_cannot_be_run_on_test_platform(). Note that simply mocking SetupClass.functon_cannot_be_run_on_test_platform will not solve the problem, because reasons.

ATM, I figure the only way to get around this mess is to somehow override SetupClass.set_some_stuff. I cannot simply mock the entire class, because UUT relies heavily on its other functionality as well. I need everything to work as is, except this one method and I need that method to be able to access, self in the same context as originally intend.

I tried various things involving subclassing and mock.return_value etc. I'd rather not recall the pain that caused. :p

My kingdom for test-driven code in the first place! This code contains a convolution of co-dependencies. :-/

Upvotes: 1

Views: 3514

Answers (1)

gruvin
gruvin

Reputation: 51

Apart from the multiple errors in the example code I wrote up off the top of my head at the cafe ...

The problem was (mostly) that I was using return_value instead of side_effect to 'replace' the patched class with my own subclass.

My need, re-stated perhaps more clearly now, is to override a single method, set_some_stuff, in a class within the unit under test (UUT) but without mocking the class.

The method issues several 'self.foo = bar' statements, which I want to change for testing purposes. Thus, mocking the method's (unused) return value is not enough ... and patch.object seems to lose class context, "'self' unknown" or the like.

Finally, here is the working code, doing what I need ...

import unittest
import mock

class SetupClass(object):
    def set_some_stuff(self):
        data_list = ['data not available', 'on test_platform'] # CRASH!
        self.something = data_list[0]
        self.something_else = data_list[1]

class UUT:
    def __init__(self):
        self.config = SetupClass()
        self.config.set_some_stuff()
        assert self.config.something == 'foo' # <-- used to crash before here ...
        self.got_here = True # ... but now the override method from
                             # WedgeClass is being used! :=)

"""
Subclass the original class, overriding just the method in question.  Then set
the subclass as the side_effect of the patched original, effectively replacing it.
"""
class WedgeClass(SetupClass):
    def set_some_stuff(self):
        self.something = 'foo'
        pass # I'm a Python newbie, in too deep

class UnitTests(unittest.TestCase):

    @mock.patch(__module__+'.SetupClass')
    def test_UUT(self, mock1):
        wedge_class = WedgeClass()
        mock1.side_effect = WedgeClass # <--- Ureka! 'side_effect' not 'return_value' was the ley!

        uut = UUT()
        self.assertTrue(uut.got_here)

Upvotes: 1

Related Questions