Está en la página 1de 55

To

MO CK

or not to

MO CK

that is th e question

To

MO CK

or not to

MO CK

that is th e question

a treatise narrated by

ANA BALICA

from Potato of Londontowne.

@anabalica

Thou
shalt
write
tests.

William Shakespeare

Chapter one

Mocks simulate
the looks and behaviour
of real objects

fig.1

fig.2

REAL

MO CK

mocks != stubs

Stubs

Mocks

Setup

Setup

Test

Setup expectations

Verify state

Test

Teardown

Verify expectations

Verify state

Teardown

unittest.mock
mock

# Python 3.3
# Python 2.x

Mock()

M o ck
>>> from mock import Mock
>>> m = Mock()
>>> m.foo = 1
>>> m.foo
1
>>> m.bar
<Mock name='mock.bar' id='4310136016'>

Mock()
MagicMock()

__lt__

__str__

__gt__

__int__

__len__

__hash__

__iter__

__exit__

__bool__

__sizeof__

Mock()
MagicMock()
patch()

Pat ching
from mock import patch
with patch('rainbow.Pony') as MockPony:
MockPony.return_value = 42

Pat ching
'rainbow.Pony'
# creatures.py

# rainbow.py

class Pony:
pass

from creatures import Pony


pony = Pony()

Mock the object where its used,


not where it came from

Chapter two

Go o d
mo ck s

System calls
os.environ
with patch.dict('os.environ', {'ANDROID_ARGUMENT': ''}):
pf = Platform()
self.assertTrue(pf == android')

Streams
sys.stdout
@mock.patch('sys.stdout', new_callable=six.StringIO)
def test_print_live_refs_empty(self, stdout):
trackref.print_live_refs()
self.assertEqual(stdout.getvalue(),
'Live References\n\n\n')

Networking
request.urlopen
@patch('django.utils.six.moves.urllib.request.urlopen')
def test_oembed_photo_request(self, urlopen):
urlopen.return_value = self.dummy_response
result = wagtail_oembed("http://www.youtube.com/watch/")
self.assertEqual(result['type'], 'photo')

IO operations
json.loads
@patch.object(DataLoader, '_get_file_contents')
def test_parse_json_from_file(self, mock_def):
mock_def.return_value = ('{"a": 1, "b": 2, "c": 3}', True)
output = self._loader.load_from_file('dummy_json.txt')
self.assertEqual(output, dict(a=1, b=2, c=3))

Clocks, time, timezones


time.sleep
@mock.patch('time.sleep')
def test_500_retry(self, sleep_mock):
self.set_http_response(status_code=500)
# Create a bucket, a key and a file
with self.assertRaises(BotoServerError):
k.send_file(fail_file)

Unpredictable results
random.random
with mock.patch('random.random', return_value=0.0):
with self.assertChanges(get_timeline_size, before=10, after=5):
backend.add(timeline, next(self.records))

System calls

Streams

Networking

IO operations

Clocks, time,
timezones

Unpredictable
results

Why we like them

Save time

Make impossible possible

Exclude external dependencies

Chapter three

Bad
mo ck s

Problems?
with patch.object(Pony, 'save_base') as mock_save:
form = PonyForm(instance=pony, data=data)
self.assertTrue(form.is_valid())
saved_pony = form.save()
self.assertTrue(saved_pony.age, 3)
mock_save.assert_called_once()

Problems?
with patch.object(Pony, 'save_base') as mock_save:
form = PonyForm(instance=pony, data=data)
self.assertTrue(form.is_valid())
saved_pony = form.save()
self.assertTrue(saved_pony.age, 3)
mock_save.assert_called_once()

Problems?
with patch.object(Pony, 'save_base') as mock_save:
form = PonyForm(instance=pony, data=data)
self.assertTrue(form.is_valid())
saved_pony = form.save()
self.assertEqual(saved_pony.age, 3)
mock_save.assert_called_once()

Problems?
with patch.object(Pony, 'save_base') as mock_save:
form = PonyForm(instance=pony, data=data)
self.assertTrue(form.is_valid())
saved_pony = form.save()
self.assertEqual(saved_pony.age, 3)
mock_save.assert_called_once()

Problems?
with patch.object(Pony, 'save_base') as mock_save:
form = PonyForm(instance=pony, data=data)
self.assertTrue(form.is_valid())
saved_pony = form.save()
self.assertEqual(saved_pony.age, 3)
mock_save.assert_called_twice()

Problems?
with patch.object(Pony, 'save_base') as mock_save:
form = PonyForm(instance=pony, data=data)
self.assertTrue(form.is_valid())
saved_pony = form.save()
self.assertEqual(saved_pony.age, 3)
mock_save.make_me_sandwich()

\_( )_/

Solution 1
with patch.object(Pony, 'save_base') as mock_save:
form = PonyForm(instance=pony, data=data)
self.assertTrue(form.is_valid())
saved_pony = form.save()
self.assertEqual(saved_pony.age, 3)
mock_save.assert_called_once_with()

Solution 2
with patch.object(Pony, 'save_base') as mock_save:
form = PonyForm(instance=pony, data=data)
self.assertTrue(form.is_valid())
saved_pony = form.save()
self.assertEqual(saved_pony.age, 3)
self.assertEqual(mock_save.call_count, 1)

Failure
with patch.object(Pony, 'save_base') as mock_save:
form = PonyForm(instance=pony, data=data)
self.assertTrue(form.is_valid())
saved_pony = form.save()
self.assertEqual(saved_pony.age, 3)
self.assertEqual(mock_save.sandwich_count, 1)

Solution 3

Test Driven
Development

Problems?
with patch.object(Pony, 'save_base') as mock_save:
form = PonyForm(instance=pony, data=data)
self.assertTrue(form.is_valid())
saved_pony = form.save()
self.assertEqual(saved_pony.age, 3)
self.assertEqual(mock_save.call_count, 1)

Tests
pass?

SHIP IT!

Mayb e
its incomple te?

Integration tests
c = Client()
response = c.post('/pony/', {'age': 1})
self.assertEqual(response.status_code, 201)

Unit tests

Integration
tests

Mocks

Only mock types


that you own

Building on
Third-Party
Code

Application
objects
Adapter
layer
3rd party
API

Mock this

Application
objects
Test
this
cluster

Adapter
layer
3rd party
API

Conclusions

Mo ck s
can be
dange ro u s

Passing faulty tests give a


fal se se nse of se curity

The end

También podría gustarte