Skip to main content

Mocking

70 % Unit tests 20 % Integration tests 10 % UI tests

  • Mock (MagicMock), Build-in as of Python3.3
  • flexmock
  • mox
  • Mocker
  • dingus
  • fudge
  • MiniMock
def get_next_person(user):
person = get_random_person()
while person in user['people_seen']:
person = get_random_person()
return person

Problem: Dependency on get_random_person Solution: Mock all dependencies (get_random_person)

from mock import patch

@patch("application.get_random_person")
def test_new_person(get_random_person):
# arrange
user = {'people_seen': []}
expceted_person = 'Katie'
get_random_person.return_value = 'Katie'

# action
actual_persion = get_next_person(user)

# assert
assert_equals(actual_person, expected_person)

Problem: What if you want the method everytime something different each call?

@patch.object(Application, "get_random_person")
def test_experienced_user(mock_get_rand_person):
# arrange
app = Application()
user = {'people_seen': ['Sarah', 'Mary']}
expected_person = 'Katie'
mock_get_rand_person.side_effect = ['Mary', 'Sarah', 'Katie']

# action
actual_person = app.get_next_person(user)

# assert
assert_equals(actual_person, expected_person)

Verifying Parameters

def evaluate(person1, person2):
if person1 in person2['likes']:
send_email(person1)
send_email(person2)
elif person1 in person2['dislikes']:
let_down_gently(person1)
elif person1 not in person2['likes'] \
and person1 not in person2['dislikes']:
give_it_time(person1)
@patch("application.send_email")
@patch("application.let_down_gently")
@patch("application.give_it_time")
def test_person2_dislikes_person1(mock_send_email, mock_let_down, mock_give_it_time):
# arrange
person1 = 'Bill'
person2 = {
'likes': ['Sam', 'Joey'],
'dislikes': ['Bill']
}

# action
evaluate(person1, person2)

mock_let_down.assert_called_once_with(person1)
assert_equals(mock_give_it_time.call_count, 0)
assert_equals(mock_send_email.call_count, 0)

How to test this:

def get_json(filename):
try:
return json.loads(open(filename).read())
expect (IOError, ValueError):
return ""
@patch("__buildin__.open")
def test_get_valid_json(mock_open):
# arrange
filename = "does_not_exist.json"
mock_file = Mock()
mock_file.read.return_value = '{"foo": "bar"}'
mock_open.return_value = mock_file

# action
actual_result = get_json(filename)

# assert
assert_equals({u'foo': u'bar'}, actual_result)

Mock exceptions

@patch("__buildin__.open")
def test_get_json_ioerror(mock_open):
# arrange
filename = "does_not_exist.json"
mock_open.side_effect = IOError

# action
actual_result = get_json(filename)

# assert
assert_equals('', actual_result)
@patch("__buildin__.open")
@patch("json.loads")
def test_get_json_value_error(mock_open, mock_loads):
# arrange
filename = "does_not_exist.json"
mock_file = Mock()
mock_file.read.return_value = '{"foo": "bar"}'
mock_open.return_value = mock_file
mock_loads.side_effect = ValueError

# action
actual_result = get_json(filename)

# assert
assert_equals('', actual_result)

Mock global variable

my_pkg/adder.py
from filelock import Timeout, FileLock

lock = FileLock("/tmp/lock-file.lock")
def add(a, b):
with lock.acquire(timeout=10):
return a + b
my_pkg/test_adder.py
@mock.patch("my_pkg.adder.lock")
def test_it_should_add_two_numbers(mocked_lock):
my_pkg.add(1, 2)
mocked_lock.acquire.assert_called_once_with(timeout=10)

Mock function variable

my_pkg/adder.py
from filelock import Timeout, FileLock

def add(a, b):
lock = FileLock("/tmp/lock-file.lock")
with lock.acquire(timeout=10):
return a + b
my_pkg/test_adder.py
@mock.patch("my_pkg.adder.FileLock")
def test_it_should_add_two_numbers(mocked_filelock):
instance = mocked_filelock.return_value
my_pkg.add(1, 2)
instance.acquire.assert_called_once_with(timeout=10)

Source