Mock a Class, but not one of its functions - python

When I import MyApp from app.py, a instance of the SerialConnection class
is created immediately. I want to mock the SerialConnection class but I still need a function from within this SerialConnection class.
app.py
# A module that creates strings that is sent via SerialConnection
from myserial import SerialConnection
class MyApp():
global ser
ser = SerialConnection() # ---> Needs to be mocked
def __init__(self):
pass
#ser.decorator # ---> Needs to be by-passed
def myfunc(self):
return 'testing1234'
myserial.py
# A module to write and read to a serial/UART buffer
from functools import wraps
import serial
class SerialConnection():
def __init__(self):
""" Initilize the Serial instance. """
self.ser = serial.Serial(port=2, baudrate=9600)
def decorator(self, func):
#wraps(func)
def wrapper_func(*args):
return func(*args)
return wrapper_func
test_app.py
# This file tests the function "myfunc" from "MyApp" class.
from patch import mock
#patch('myserial.SerialConnection')
def test_myfunc(mock_class):
# I can now import MyApp successfuly...
from app import MyApp
# ..but I need to bypass the decorator myserial.SerialConnection.decorator
# do I add a by-pass decorator to the "mock_class"?
# myfunc() turns out to be mocked and not a real function
assert MyApp().myfunc() == 'testing1234'

Related

Python: setting default side_effect with create_autospec on class instance

I am trying to use side_effect with unittest.mock.create_autospec on class to set default instance call behavior to raise NotImplementedError.
The problem I am facing is:
I do not want exception to be raise on class __init__.
I do not want to set explicitly all my methods side effect.
I want to use a pytest.fixture in order to make my mock reusable through various tests.
Here a code sample of what I am trying to achieve.
# module.py
class MyClass:
def __init__(self, value):
self.value = value
def compute(self):
return self.value
def foo():
instance = MyClass(42)
return instance.compute()
# test_module.py
from unittest.mock import create_autospec
import module
import pytest
#pytest.fixture(autouse=True)
def my_class(monkeypatch):
# Help me HERE to set side_effect, bellow tests will not work with this settings.
spec_cls = create_autospec(module.MyClass, side_effect=NotImplementedError)
monkeypatch.setattr(module, "MyClass", spec_cls)
return spec_cls("<whatever>")
def test_foo():
with pytest.raises(NotImplementedError):
module.foo()
def test_bar(my_class):
my_class.compute.return_value = 24
assert module.foo() == 24
Not need to use autospec :
import unittest.mock as mocking
import pytest
import so71018132_module as module
#pytest.fixture(autouse=True)
def fake_my_class():
with mocking.patch.object(module.MyClass, "compute") as compute_mock:
compute_mock.side_effect = NotImplementedError # default behaviour
yield compute_mock
def test_foo():
with pytest.raises(NotImplementedError):
module.foo()
def test_bar(fake_my_class):
fake_my_class.side_effect = [24]
# or alternatively, clear the side_effect then set a return_value :
# fake_my_class.side_effect = None
# fake_my_class.return_value = 24
assert module.foo() == 24
passes the 2 tests.
I completely changed the fixture to use unittest.mock.object.patch on the compute method of MyClass so just that is mocked, the rest of the class is used ordinarily.
Also, I had to slightly adjust the test_bar code to correctly alter the mock behavior.

Replacing python with_statement by setUp and tearDown in Unittest

In a test suite I have some code organized as below, context is some persistent object that is deleted when exiting the with block:
class Test(TestCase):
def test_action1(self):
with create_context() as context:
context.prepare_context()
context.action1()
self.assertTrue(context.check1())
def test_action2(self):
with create_context() as context:
context.prepare_context()
context.action2()
self.assertTrue(context.check2())
It's obvious that code has some repetition of setup boilerplate in both tests, hence I would like to use setUp() and tearDown() methods to factorize that boilerplate.
But I don't know how to extract the with_statement. What I came up with is something like this:
class Test(TestCase):
def setUp(self):
self.context = create_context()
self.context.prepare_context()
def tearDown(self):
del self.context()
def test_action1(self):
self.context.action1()
self.assertTrue(self.context.check1())
def test_action2(self):
self.context.action2()
self.assertTrue(self.context.check2())
But I believe this is not really equivalent when test fails, also having to put an explicit delete in tearDown() doesn't feel right.
What is the correct way to change my with_statement code to setUp() and tearDown() style ?
I'm not 100% sure about the setUp() and tearDown(), but the methods defined in context managers __enter__ and __exit__ sound like they do what you want them to do (just with different names):
class ContextTester():
def __enter__(self):
self.context = create_context()
self.context.prepare_context()
return self.context
def __exit(self, exc_type, exc_value, exc_traceback):
self.context.close()
def Test(TestCase):
def test_action1(self):
with ContextTester() as context:
context.action1()
self.assertTrue(context.check1())
You might want to use contextlib.ExitStack for that.
A context manager that is designed to make it easy to programmatically combine other context managers and cleanup functions, especially those that are optional or otherwise driven by input data.
import contextlib
from unittest import TestCase
class Test(TestCase):
def setUp(self) -> None:
stack = contextlib.ExitStack()
self.context = stack.enter_context(create_context()) # create_context is your context manager
self.addCleanup(stack.close)
def test_action1(self):
self.context.prepare_context()
self.context.action1()
self.assertTrue(self.context.check1())
or if you want to have some control over the teardown or use multiple context managers this will be better
import contextlib
from unittest import TestCase
class Test(TestCase):
def setUp(self):
with contextlib.ExitStack() as stack:
self.context = stack.enter_context(create_context()) # create_context is your context manager
self._resource_stack = stack.pop_all()
def tearDown(self):
self._resource_stack.close()
def test_action1(self):
self.context.prepare_context()
self.context.action1()
self.assertTrue(self.context.check1())

mocking in multithreaded context

I would like to write some integration tests for some classes that start in multithreaded context. The problem is one of the classes has some external library which I want to mock.
Example:
Foo.py
import threading
from external_lib import function_i_want_to_mock
class Foo(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
print(self.run_function())
def run_function(self):
return function_i_want_to_mock()
Bar.py
import threading
from foo import Foo
class Bar(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
foo = Foo()
foo.start()
test_mock_thread.py
from unittest.mock import patch
from bar import Bar
def test_run():
with patch("foo.function_i_want_to_mock"):
bar = Bar()
bar.start()
bar.join()
if __name__ == '__main__':
test_run()
When I run this test, the function does not get mocked and it is being imported normally. Is there a way to pass the mocked version through the threads?

Python class mock. How can I get mock-class method calls information

I have a class named Client providing some service by its getResponse method. This class is used by other classes.
I make unit testing for class Driver who uses the Client class.
By using mock.patch, I replace the Client class by mock class called MockClient
which has the same getResponse method returning some predefined values.
It works great. But now I want to test parameters the getRsponse method was called with.
I want to do it by using the *assert_has_calls* method.
Did not find how to do it. Please advice.
Class under test:
# Driver and its Client
class Driver:
def __init__ (self):
self.client = Client()
def call(self, param):
return self.client.getResponse(param)
class Client:
def getResponse(self, param):
return 'original'
This is the Test class with the mock class:
import unittest
import mock
import driver
from driver import Driver
from driver import Client
class MockClient:
def getResponse(self,param):
return 'mock class'
class TestIt(unittest.TestCase):
def setUp(self):
self.mock_client = mock.patch('driver.Client',create=True, new=MockClient)
self.mock_client.start()
def tearDown(self):
self.mock_client.stop()
def test_call(self):
driver = Driver()
result = driver.call('test')
self.assertEqual(result, 'mock class')
assert_has_calls expects a list of call objects that it can compare to.
You can get a call object by calling the mock.call helper function with your expected arguments and keyword arguments.
This example is straight from the documentation and illustrates the usage quite well:
mock = Mock(return_value=None)
mock(1)
mock(2)
mock(3)
mock(4)
calls = [call(2), call(3)]
mock.assert_has_calls(calls)
calls = [call(4), call(2), call(3)]
mock.assert_has_calls(calls, any_order=True)

unittest, running test from other file

I can't seem to get the Test1.test_something() in test2 to work.. not sure if it's because they are both inheriting from the same base?
Helper.py:
class baseTest(unittest.TestCase):
def setUp(self, param="Something"):
print param
pass
Test1.py
from Helper import baseTest
class test1(baseTest):
def setUp(self):
super(test1, self).setUp('foo')
def test_something(self):
assert 1 == 1, "One does not equal one."
Test2.py
from Helper import baseTest
import Test1
class test2(baseTest):
def setUp(self):
super(test2, self).setUp('bar')
def test_something(self):
Test1.test_somehing()
Now, I had this working previously, when I had the setUp for test1 and test2 within their classes, but once I had them both inherit from baseTest, I started getting a unbound method <method> must be called with Test instance as first argument (got nothing instead). Any suggestions?
The problem is that Test1.test_something() is an instance method, not a class method. So you can't just call it like that (besides, even if it is class method, it should have been Test1.test1.test_something).
One way to do it (without messing around with the unittest.TestCase mechanism):
Test2.py
import Test1
class test2(Test1.test1):
# whatever else
And you're done, test2 inherits Test1.test1.test_something() automatically. If you need your test2's test_something to do extra stuff, just do super(test2, self).test_something() within your overridden definition of test_something in test2 class.
Move the tests that are shared by both Test1 and Test2 classes into BaseTest:
test.py:
import unittest
import sys
class BaseTest(unittest.TestCase):
def setUp(self, param="Something"):
print param
pass
def test_something(self):
assert 1 == 1, "One does not equal one."
class Test1(BaseTest):
def setUp(self):
super(Test1, self).setUp('foo')
test2.py:
import test
import unittest
import sys
class Test2(test.BaseTest):
def setUp(self):
super(Test2, self).setUp('bar')

Categories