After reading about testing private methods in Python, specifically referring to this accepted answer, it appears that it is best to just test the public interface. However, my class looks like this:
class MyClass:
def __init__(self):
# init code
def run(self):
self.__A()
self.__B()
self.__C()
self.__D()
def __A(self):
# code for __A
def __B(self):
# code for __B
def __C(self):
# code for __C
def __D(self):
# code for __D
Essentially, I created a class to process some input data through a pipeline of functions. In this case, it would be helpful to test each private function in turn, without exposing them as public functions. How does one go about this, if a unit test can't execute the private function?
Python does some name mangling when it puts the actually-executed code together. Thus, if you have a private method __A on MyClass, you would need to run it like so in your unit test:
from unittest import TestCase
class TestMyClass(TestCase):
def test_private(self):
expected = 'myexpectedresult'
m = MyClass()
actual = m._MyClass__A
self.assertEqual(expected, actual)
The question came up about so-called 'protected' values that are demarcated by a single underscore. These method names are not mangled, and that can be shown simply enough:
from unittest import TestCase
class A:
def __a(self):
return "myexpectedresult"
def _b(self):
return "a different result"
class TestMyClass(TestCase):
def test_private(self):
expected = "myexpectedresult"
m = A()
actual = m._A__a()
self.assertEqual(expected, actual)
def test_protected(self):
expected = "a different result"
m = A()
actual = m._b()
self.assertEqual(expected, actual)
# actual = m._A__b() # Fails
# actual = m._A_b() # Fails
First of all, you CAN access the "private" stuff, can't you? (Or am I missing something here?)
>>> class MyClass(object):
... def __init__(self):
... pass
... def __A(self):
... print('Method __A()')
...
>>> a=MyClass()
>>> a
<__main__.MyClass object at 0x101d56b50>
>>> a._MyClass__A()
Method __A()
But you could always write a test function in MyClass if you have to test the internal stuff:
class MyClass(object):
...
def _method_for_unit_testing(self):
self.__A()
assert <something>
self.__B()
assert <something>
....
Not the most elegant way to do it, to be sure, but it's only a few lines of code at the bottom of your class.
Probably you should just test the run() method. Most classes will have internal methods -- and it does not really matter in this case whether or not all the code in __A(), __B(), __C,() and __D() is actually in run() or not. If you suspect or find problems, then you might want to switch to your debugger aspect and test the private methods.
Related
I am Python newbie and trying to understand how to mock methods in UTs.
Here is my Test
test_module2.py
from module2 import A
import unittest
from unittest.mock import patch, MagicMock
class TestBulkLoad(unittest.TestCase):
#patch('simple_module.SimpleModuleClass')
def test_fun(self, a):
a.simpleFun = MagicMock(return_value=3)
testA = A()
testA.fun()
assert a.simpleFun.called
and I want to check that dependent SimpleModuleClass was mocked and invoked
module2.py
from simple_module import SimpleModuleClass
class A:
def fun(self):
print('I am from A class')
thing = SimpleModuleClass()
thing.simpleFun()
return -1
simple_module.py
class SimpleModuleClass:
def simpleFun(self):
print("I am from SimpleModuleClass")
return 0
I get AssertionError. Could you help me understand how to fix this?
a.simpleFun = MagicMock(return_value=3)
This line is only modifying the method of your mock object a (which you really should rename simpleModuleMock, since it's a mock of a SimpleModule class and not an A). It doesn't mock every instance of SimpleModule everywhere in your code, hence why an instance of SimpleModule is instantiated in A.fun() and not an instance of a mock of SimpleModule.
I would use dependency injection and pass an instance of SimpleModule to A.fun() instead of instantiating it inside of it, so that you can pass a mock. You could also make thing a public variable like so:
test_module2.py:
class TestBulkLoad(unittest.TestCase):
#patch('simple_module.SimpleModuleClass')
def test_fun(self, simpleModuleMock):
simpleModuleMock.simpleFun = MagicMock(return_value=3)
testA = A()
A.thing = simpleModuleMock
assert(testA.fun() == -1)
# assert simpleModule.simpleFun.called
assert isinstance(simpleModuleMock.simpleFun, MagicMock)
module2.py:
class A:
thing = SimpleModuleClass()
def fun(self):
print('I am from A class')
self.thing.simpleFun()
return -1
Note that there may exist better solutions with Python, but if there are, 1. I don't know of them, and 2. I doubt they're good practice.
I have some code that creates instances from a list of classes that is passed to it. This cannot change as the list of classes passed to it has been designed to be dynamic and chosen at runtime through configuration files). Initialising those classes must be done by the code under test as it depends on factors only the code under test knows how to control (i.e. it will set specific initialisation args). I've tested the code quite extensively through running it and manually trawling through reams of output. Obviously I'm at the point where I need to add some proper unittests as I've proven my concept to myself. The following example demonstrates what I am trying to test:
I would like to test the run method of the Foo class defined below:
# foo.py
class Foo:
def __init__(self, stuff):
self._stuff = stuff
def run():
for thing in self._stuff:
stuff = stuff()
stuff.run()
Where one (or more) files would contain the class definitions for stuff to run, for example:
# classes.py
class Abc:
def run(self):
print("Abc.run()", self)
class Ced:
def run(self):
print("Ced.run()", self)
class Def:
def run(self):
print("Def.run()", self)
And finally, an example of how it would tie together:
>>> from foo import Foo
>>> from classes import Abc, Ced, Def
>>> f = Foo([Abc, Ced, Def])
>>> f.run()
Abc.run() <__main__.Abc object at 0x7f7469f9f9a0>
Ced.run() <__main__.Abc object at 0x7f7469f9f9a1>
Def.run() <__main__.Abc object at 0x7f7469f9f9a2>
Where the list of stuff to run defines the object classes (NOT instances), as the instances only have a short lifespan; they're created by Foo.run() and die when (or rather, sometime soon after) the function completes. However, I'm finding it very tricky to come up with a clear method to test this code.
I want to prove that the run method of each of the classes in the list of stuff to run was called. However, from the test, I do not have visibility on the Abc instance which the run method creates, therefore, how can it be verified? I can't patch the import as the code under test does not explicitly import the class (after all, it doesn't care what class it is). For example:
# test.py
from foo import Foo
class FakeStuff:
def run(self):
self.run_called = True
def test_foo_runs_all_stuff():
under_test = Foo([FakeStuff])
under_test.run()
# How to verify that FakeStuff.run() was called?
assert <SOMETHING>.run_called, "FakeStuff.run() was not called"
It seems that you correctly realise that you can pass anything into Foo(), so you should be able to log something in FakeStuff.run():
class Foo:
def __init__(self, stuff):
self._stuff = stuff
def run(self):
for thing in self._stuff:
stuff = thing()
stuff.run()
class FakeStuff:
run_called = 0
def run(self):
FakeStuff.run_called += 1
def test_foo_runs_all_stuff():
under_test = Foo([FakeStuff, FakeStuff])
under_test.run()
# How to verify that FakeStuff.run() was called?
assert FakeStuff.run_called == 2, "FakeStuff.run() was not called"
Note that I have modified your original Foo to what I think you meant. Please correct me if I'm wrong.
I have class with few methods. Let's say A
file a.py
class A:
def foo():
...
def bar():
...
during normal runtime (not testing) I use it in different modules, like that.
file functions.py
from a import A
def sample_function():
a_instance = A()
result = a_instance.foo()
return result
But during tests I would like to replace it with different class, let's say MockA.
file mock_a.py
class MockA:
# same methods name, but with different implementation
def foo():
...
def bar():
...
Now I would like to test module with some functionality
tests
from functions import sample_function
def test_sample_function():
assert sample_function() == expected_output
The QUESTION is:
Can I somehow "globally" set alias A = MockA (or do this in other way), so that during tests sample_function use functionality from MockA?
sample_function uses whatever A is bound to in the global namespace of the module functions. You can rebind your own class to that name.
from functions import one_function
class MockA:
...
functions.A = MockA
def test_sample_function():
assert sample_function() == expected_output
This is exactly what unittest.mock.patch is for
How about you use:
tests
from unittest.mock import patch
from mock_a import MockA
from functions import sample_function
#patch('functions.A', new_callable=MockA)
def test_sample_function(mocked_A):
assert sample_function() == expected_output
Try changeing this:
from a import A
into this:
from mock_a import MockA as A
First correct me in comments if I am wrong, but is it a mistake that you wrote one_function in your last code snippet, where you should have written sample_function.
I think your concern is that you don't want to change the functions.py code by replacing a_instance = A() with a_instance = MockA() everywhere in the code. So just make mock_a.py with same class name class A: and same methods name, but different implementations(like you said). All you will have to change in your functions.py code is from mock_a import A instead all all instance of class A() to class MockA(). This way I think your tests should work perfectly.
Im trying to unittest some_function which is MyClass instance method that requires someapi.api instance . How do I patch self.api.something1.something2(foo) with some return value?
import someapi
class MyClass(object):
def __init__(self,a,b):
self.a = a
self.b = b
self.api = someapi.api(self.a, self.b)
def some_function(self, foo):
result = self.api.something1.something2(foo)
new_result = dosomething(result)
return new_result
So really what I want is to mock response of this api so I can test that dosomething(result) does what I want.
#mock.patch('self.api.something1.something2', side_effect='something')
def testGet_circuits(self,pymock):
result = some_function('foobar')
expected_result= 'something'
self.assertEqual(result, 'expected_result')
I tried
#mock.patch('someapi.api')
def testSome_function(self,someapi_mock):
api = MyClass('a','b')
result = api.some_function('foo')
self.assertEqual(result,'newfoo')
What I'm strugling with is how to mock self.api.something1.something2(foo) inside some_function :(
You are not setting up your mock properly. I crafted an example using your code and managed to put together a test method that works. Ultimately, what is happening here is that you are not telling your mocks how to behave when the code runs. You start off probably mocking in the right place, but beyond that, the mock itself no longer has any of the attributes you are trying to access in order to allow the code to run. I'll go through the code to help illustrate:
class TestStuff(unittest.TestCase):
#mock.patch('real_code.someapi.api', autospec=True)
def testSome_function(self, someapi_mock):
someapi_mock.return_value.something1 = mock.Mock(spec=Something)
someapi_mock.return_value.something1.something2.return_value = 'newfoo'
api = MyClass('a', 'b')
result = api.some_function('foo')
self.assertEqual(result, 'newfoo')
First thing, notice that I'm mocking more with respect to where I am testing, which is important to keep in mind, and you should read about here.
For the mock configuration issue, I can only assume from your real code, api.something1.something2 indicates that something1 is holding the instance of some other class that gives you access to the something2 method. So, the example is being illustrated with that assumption.
Now, as you can see within the first line of the method, what I'm doing is telling my mock to ensure it has the something1 attribute. It is important to remember that when you are mocking, and even when you set the spec and/or autospec (as I have used in my example), you don't get access to the attributes created in your __init__. So you need to provide them in your mock per my example.
The second line now goes the next step to mock out the something2 method behaviour you are trying to get a result from. With this done, when your real code is called, it should go through the expected behaviour you set up and return the expected newfoo value.
To further help, here is the exact code I used to help put together that functional test:
real_code.py
import someapi
class MyClass(object):
def __init__(self,a,b):
self.a = a
self.b = b
self.api = someapi.api(self.a, self.b)
def some_function(self, foo):
result = self.api.something1.something2(foo)
new_result = dosomething(result)
return new_result
def dosomething(foo):
return foo
someapi.py
class api:
def __init__(self, a, b):
self.a = a
self.b = b
self.something1 = Something()
class Something:
def something2(self, some_arg):
return some_arg
Thanks for all the answers definitely helped me
I ended up doing below and worked like a charm . This whole mock thing looks like rabbit hole and i need to go deeper to fully understand.
#mock.patch('someapi.api')
def testSome_function(self,someapi_mock):
someapi_mock = return_value.something1.something2.return_value = 'mocked_value'
api = MyClass('a','b')
result = api.some_function('foo')
self.assertEqual(result,'newfoo')
I am using mock with Python and was wondering which of those two approaches is better (read: more pythonic).
Method one: Just create a mock object and use that. The code looks like:
def test_one (self):
mock = Mock()
mock.method.return_value = True
self.sut.something(mock) # This should called mock.method and checks the result.
self.assertTrue(mock.method.called)
Method two: Use patch to create a mock. The code looks like:
#patch("MyClass")
def test_two (self, mock):
instance = mock.return_value
instance.method.return_value = True
self.sut.something(instance) # This should called mock.method and checks the result.
self.assertTrue(instance.method.called)
Both methods do the same thing. I am unsure of the differences.
Could anyone enlighten me?
mock.patch is a very very different critter than mock.Mock. patch replaces the class with a mock object and lets you work with the mock instance. Take a look at this snippet:
>>> class MyClass(object):
... def __init__(self):
... print 'Created MyClass#{0}'.format(id(self))
...
>>> def create_instance():
... return MyClass()
...
>>> x = create_instance()
Created MyClass#4299548304
>>>
>>> #mock.patch('__main__.MyClass')
... def create_instance2(MyClass):
... MyClass.return_value = 'foo'
... return create_instance()
...
>>> i = create_instance2()
>>> i
'foo'
>>> def create_instance():
... print MyClass
... return MyClass()
...
>>> create_instance2()
<mock.Mock object at 0x100505d90>
'foo'
>>> create_instance()
<class '__main__.MyClass'>
Created MyClass#4300234128
<__main__.MyClass object at 0x100505d90>
patch replaces MyClass in a way that allows you to control the usage of the class in functions that you call. Once you patch a class, references to the class are completely replaced by the mock instance.
mock.patch is usually used when you are testing something that creates a new instance of a class inside of the test. mock.Mock instances are clearer and are preferred. If your self.sut.something method created an instance of MyClass instead of receiving an instance as a parameter, then mock.patch would be appropriate here.
I've got a YouTube video on this.
Short answer: Use mock when you're passing in the thing that you want mocked, and patch if you're not. Of the two, mock is strongly preferred because it means you're writing code with proper dependency injection.
Silly example:
# Use a mock to test this.
my_custom_tweeter(twitter_api, sentence):
sentence.replace('cks','x') # We're cool and hip.
twitter_api.send(sentence)
# Use a patch to mock out twitter_api. You have to patch the Twitter() module/class
# and have it return a mock. Much uglier, but sometimes necessary.
my_badly_written_tweeter(sentence):
twitter_api = Twitter(user="XXX", password="YYY")
sentence.replace('cks','x')
twitter_api.send(sentence)
Key points which explain difference and provide guidance upon working with unittest.mock
Use Mock if you want to replace some interface elements(passing args) of the object under test
Use patch if you want to replace internal call to some objects and imported modules of the object under test
Always provide spec from the object you are mocking
With patch you can always provide autospec
With Mock you can provide spec
Instead of Mock, you can use create_autospec, which intended to create Mock objects with specification.
In the question above the right answer would be to use Mock, or to be more precise create_autospec (because it will add spec to the mock methods of the class you are mocking), the defined spec on the mock will be helpful in case of an attempt to call method of the class which doesn't exists ( regardless signature), please see some
from unittest import TestCase
from unittest.mock import Mock, create_autospec, patch
class MyClass:
#staticmethod
def method(foo, bar):
print(foo)
def something(some_class: MyClass):
arg = 1
# Would fail becuase of wrong parameters passed to methd.
return some_class.method(arg)
def second(some_class: MyClass):
arg = 1
return some_class.unexisted_method(arg)
class TestSomethingTestCase(TestCase):
def test_something_with_autospec(self):
mock = create_autospec(MyClass)
mock.method.return_value = True
# Fails because of signature misuse.
result = something(mock)
self.assertTrue(result)
self.assertTrue(mock.method.called)
def test_something(self):
mock = Mock() # Note that Mock(spec=MyClass) will also pass, because signatures of mock don't have spec.
mock.method.return_value = True
result = something(mock)
self.assertTrue(result)
self.assertTrue(mock.method.called)
def test_second_with_patch_autospec(self):
with patch(f'{__name__}.MyClass', autospec=True) as mock:
# Fails because of signature misuse.
result = second(mock)
self.assertTrue(result)
self.assertTrue(mock.unexisted_method.called)
class TestSecondTestCase(TestCase):
def test_second_with_autospec(self):
mock = Mock(spec=MyClass)
# Fails because of signature misuse.
result = second(mock)
self.assertTrue(result)
self.assertTrue(mock.unexisted_method.called)
def test_second_with_patch_autospec(self):
with patch(f'{__name__}.MyClass', autospec=True) as mock:
# Fails because of signature misuse.
result = second(mock)
self.assertTrue(result)
self.assertTrue(mock.unexisted_method.called)
def test_second(self):
mock = Mock()
mock.unexisted_method.return_value = True
result = second(mock)
self.assertTrue(result)
self.assertTrue(mock.unexisted_method.called)
The test cases with defined spec used fail because methods called from something and second functions aren't complaint with MyClass, which means - they catch bugs, whereas default Mock will display.
As a side note there is one more option: use patch.object to mock just the class method which is called with.
The good use cases for patch would be the case when the class is used as inner part of function:
def something():
arg = 1
return MyClass.method(arg)
Then you will want to use patch as a decorator to mock the MyClass.