About
Prerequisites
pip is the easiest way to install mock.pip install mock
Source codes can be found here
Basic usage
Trivial class
We need a trivial class for demonstration how to write unit tests with mock.
import sys
class Person(object):
def speak(self, message):
"""Speak out given message in stdout
Parameters:
- `message`: A string going to speak
Return:
- `self`: This instance
"""
sys.stdout.write('%s\n' % message)
return self
def yell(self, message):
"""Yell out given message in both stdout and stderr
Parameters:
- `message`: A string going to yell
Return:
- `self`: This instance
"""
sys.stdout.write('%s\n' % message)
sys.stderr.write('%s!\n' % message)
return self
Then, we would like to test whether
speak() and yell() have been print to stdout and stderr.
Test speak()
@mock.patch('use_mock.person.sys.stdout')
def test_speak(self, mockStdout):
self.myPerson.speak('haha')
mockStdout.write.assert_has_calls([
mock.call('haha\n')
])
Let’s go through this snippet line by line.
@mock.patch('use_mock.person.sys.stdout')
Mock out
sys.stdout is critical when writing unit tests as it provides an easy way for us to manipulate outcomes of running them. For example, throwing a system exception like Out Of Memory etc. We can verify our codes can survive any specified strange scenarios as a result.
Back into our case, mocking
sys.stdout allows us to capture any arguments passing to them an avoid them to be printed on console.def test_speak(self, mockStdout):
Prefix
test_ is required by the unittest library. Otherwise, it won’t count as a test method. ReferencemockStdout is the mock outcome from previous decorator. self.myPerson.speak('haha')
Runs
speak() from the instance self.myPerson with argument haha.
Expect to see
haha\n in sys.stdout.write after running this line. mockStdout.write.assert_has_calls([
mock.call('haha\n')
])
Verify argument
haha\n has been passed to sys.stdout.write.
Test yell()
@mock.patch('use_mock.person.sys')
def test_yell(self, mockSys):
self.myPerson.yell('haha')
mockSys.stdout.write.assert_has_calls([
mock.call('haha\n')
])
mockSys.stderr.write.assert_has_calls([
mock.call('haha!\n')
])
Contents are almost the same with previous section. However, there are slightly differences.
- We mock
use_mock.person.sysinstead ofuse_mock.person.sys.stdout assertruns onmockSys.stdoutandmockSys.stderr
The take away is that you can define how deep to mock by giving a concise path to
patch().
In our case, we mock out
sys directly as we need to test sys.stderr and sys.stdout.Something extra
References
- https://docs.python.org/3/library/unittest.mock.html#module-unittest.mock
- https://docs.python.org/2/library/unittest.html
- https://stackoverflow.com/questions/8522689/how-to-temporary-hide-stdout-or-stderr-while-running-a-unittest-in-python
- https://stackoverflow.com/questions/12998908/is-it-possible-to-mock-pythons-built-in-print-function
沒有留言:
張貼留言