I'm just getting started with using Python's mock library to help write more concise and isolated unit tests. My situation is that I've got a class that reads in data from a pretty hairy format, and I want to test a method on this class which presents the data in a clean format.
class holds_data(object):
def __init__(self, path):
"""Pulls complicated data from a file, given by 'path'.
Stores it in a dictionary.
"""
self.data = {}
with open(path) as f:
self.data.update(_parse(f))
def _parse(self, file):
# Some hairy parsing code here
pass
def x_coords(self):
"""The x coordinates from one part of the data
"""
return [point[0] for point in self.data['points']]
The code above is a simplification of what I have. In reality _parse
is a fairly significant method which I have test coverage for at the functional level.
I'd like to be able, however, to test x_coords
at a unit test level. If I were to instantiate this class by giving it a path, it would violate the rules of unit tests because:
A test is not a unit test if:
- It touches the filesystem
So, I'd like to be able to patch the __init__
method for holds_data
and then just fill in the part of self.data
needed by x_coords
. Something like:
from mock import patch
with patch('__main__.holds_data.__init__') as init_mock:
init_mock.return_value = None
instance = holds_data()
instance.data = {'points':[(1,1),(2,2),(3,4)]}
assert(instance.x_coords == [1,2,3])
The code above works but it feels like it's going about this test in a rather roundabout way. Is there a more idiomatic way to patch out a constructor or is this the correct way to go about doing it? Also, is there some code smell, either in my class or test that I'm missing?
Edit: To be clear, my problem is that during initialization, my class does significant amounts of data processing to organize the data that will be presented by a method like x_coords
. I want to know what is the easiest way to patch all of those steps out, without having to provide a complete example of the input. I want to only test the behavior of x_coords
in a situation where I control the data it uses.
My question of whether or not there is code smell here boils down to this issue:
I'm sure this would be easier if I refactor to have x_coords
be a stand alone function that takes holds_data
as a parameter. If "easier to tests == better design" holds, this would be the way to go. However, it would require the x_coords
function to know more about the internals of holds_data
that I would normally be comfortable with. Where should I make the trade off? Cleaner code or cleaner tests?
0, we can now mock Java constructors with Mockito. This allows us to return a mock from every object construction for testing purposes. Similar to mocking static method calls with Mockito, we can define the scope of when to return a mock from a Java constructor for a particular Java class.
Mocking is a process used in unit testing when the unit being tested has external dependencies. The purpose of mocking is to isolate and focus on the code being tested and not on the behavior or state of external dependencies.
In order to mock a constructor function, the module factory must return a constructor function. In other words, the module factory must be a function that returns a function - a higher-order function (HOF). Since calls to jest. mock() are hoisted to the top of the file, Jest prevents access to out-of-scope variables.
We can use Mockito class mock() method to create a mock object of a given class or interface. This is the simplest way to mock an object. We are using JUnit 5 to write test cases in conjunction with Mockito to mock objects.
Since you're only interested in testing one method, why don't you just mock the entire HoldsData
class and pin on it the x_coords
method?
>>> mock = MagicMock(data={'points': [(0,1), (2,3), (4,5)]})
>>> mock.x_coords = HoldsData.__dict__['x_coords']
>>> mock.x_coords(mock)
[0, 2, 4]
This way you'll have full control over the x_coords
input and output (either by side effect or return value).
Note: In py3k you could just do mock.x_coords = HoldsData.x_coords
since there are no more unbound methods
.
That could also be done in the constructor of the mock object:
MagicMock(data={'points': [(0,1), (2,3), (4,5)]}, x_coords=HoldsData.__dict__['x_coords'])
You're basically running into this issue due to this:
A test is not a unit test if:
- It touches the filesystem
If you wish to follow this rule, you should modify the _parse
method. In particular, it should not take a file as input. The task of _parse
is to parse the data but where the data comes from is not the concern of that method.
You could have a string that contains the same data which is then passed to _parse
. Similarly, the data could come from a database or somewhere else entirely. When _parse
only takes the data as input, you can unit test that method in a much easier way.
It would look something like this:
class HoldsData(object):
def __init__(self, path):
self.data = {}
file_data = self._read_data_from_file(path)
self.data.update(self._parse(file_data))
def _read_data_from_file(self, path):
# read data from file
return data
def _parse(self, data):
# do parsing
Clean code leads to clean tests of course. The best case would be to mock the data and to provide _parse
with input and then test x_coords
later. If that's not possible, I'd leave it as it is. If your six lines of mock code are the only part of the test cases that you're worried about, then you're fine.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With