[pycon2016]to mock or not to mock, that is the questions

55
To or not to that is the question MOCK MOCK

Upload: ana-balica

Post on 16-Apr-2017

531 views

Category:

Software


0 download

TRANSCRIPT

Page 1: [PyCon2016]To mock or not to mock, that is the questions

To

or not to

that is the question

MO CKMO CK

Page 2: [PyCon2016]To mock or not to mock, that is the questions

To

or not to

that is the question

MO CKMO CK

Page 3: [PyCon2016]To mock or not to mock, that is the questions

@anabalica

a treatise narrated by

from Potato of Londontowne.ANA BALICA

Page 4: [PyCon2016]To mock or not to mock, that is the questions

Thou shalt write tests.

“ ”

William Shakespeare

Page 5: [PyCon2016]To mock or not to mock, that is the questions

Chapter one

Page 6: [PyCon2016]To mock or not to mock, that is the questions

Mocks simulatethe looks and behaviour

of real objects

Page 7: [PyCon2016]To mock or not to mock, that is the questions

REALfig.1 fig.2

MOCK

Page 8: [PyCon2016]To mock or not to mock, that is the questions

mocks != stubs

Page 9: [PyCon2016]To mock or not to mock, that is the questions

✓ Setup

✓ Test

✓ Verify state

✓ Teardown

✓ Setup

✓ Setup expectations

✓ Test

✓ Verify expectations

✓ Verify state

✓ Teardown

Stubs Mocks

Page 10: [PyCon2016]To mock or not to mock, that is the questions

unittest.mock # Python 3.3 mock # Python 2.x

Page 11: [PyCon2016]To mock or not to mock, that is the questions

Mock()

Page 12: [PyCon2016]To mock or not to mock, that is the questions

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

Page 13: [PyCon2016]To mock or not to mock, that is the questions

Mock() MagicMock()

Page 14: [PyCon2016]To mock or not to mock, that is the questions

__lt__

__gt__

__len__

__iter__

__bool__

__str__

__int__

__hash__

__exit__

__sizeof__

Page 15: [PyCon2016]To mock or not to mock, that is the questions

Mock() MagicMock()

patch()

Page 16: [PyCon2016]To mock or not to mock, that is the questions

from mock import patch

with patch('rainbow.Pony') as MockPony: MockPony.return_value = 42

Patching

Page 17: [PyCon2016]To mock or not to mock, that is the questions

Patching'rainbow.Pony'

# creatures.py

class Pony: pass

# rainbow.py

from creatures import Pony pony = Pony()

Page 18: [PyCon2016]To mock or not to mock, that is the questions

Mock the object where it’s used, not where it came from

Page 19: [PyCon2016]To mock or not to mock, that is the questions

Chapter two

Page 20: [PyCon2016]To mock or not to mock, that is the questions

Good mocks

Page 21: [PyCon2016]To mock or not to mock, that is the questions

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

os.environ

System calls

Page 22: [PyCon2016]To mock or not to mock, that is the questions

@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')

sys.stdoutStreams

Page 23: [PyCon2016]To mock or not to mock, that is the questions

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')

Networking

Page 24: [PyCon2016]To mock or not to mock, that is the questions

@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))

json.loadsIO operations

Page 25: [PyCon2016]To mock or not to mock, that is the questions

@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)

Clocks, time, timezonestime.sleep

Page 26: [PyCon2016]To mock or not to mock, that is the questions

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))

random.random

Unpredictable results

Page 27: [PyCon2016]To mock or not to mock, that is the questions

✓ System calls

✓ Streams

✓ Networking

✓ IO operations

✓ Clocks, time, timezones

✓ Unpredictable results

Page 28: [PyCon2016]To mock or not to mock, that is the questions

✓ Save time

✓ Make impossible possible

✓ Exclude external dependencies

Why we like them

Page 29: [PyCon2016]To mock or not to mock, that is the questions

Chapter three

Page 30: [PyCon2016]To mock or not to mock, that is the questions

mocks Bad

Page 31: [PyCon2016]To mock or not to mock, that is the questions

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?

Page 32: [PyCon2016]To mock or not to mock, that is the questions

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?

Page 33: [PyCon2016]To mock or not to mock, that is the questions

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?

Page 34: [PyCon2016]To mock or not to mock, that is the questions

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?

Page 35: [PyCon2016]To mock or not to mock, that is the questions

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?

Page 36: [PyCon2016]To mock or not to mock, that is the questions

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()

Problems?

Page 37: [PyCon2016]To mock or not to mock, that is the questions

¯\_( )_/¯

Page 38: [PyCon2016]To mock or not to mock, that is the questions

Solution 1with 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()

Page 39: [PyCon2016]To mock or not to mock, that is the questions

Solution 2with 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)

Page 40: [PyCon2016]To mock or not to mock, that is the questions

Failurewith 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)

Page 41: [PyCon2016]To mock or not to mock, that is the questions

Solution 3

Test Driven Development

Page 42: [PyCon2016]To mock or not to mock, that is the questions

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)

Page 43: [PyCon2016]To mock or not to mock, that is the questions

Tests pass?

SHIP IT!

Page 44: [PyCon2016]To mock or not to mock, that is the questions

Maybe it’s incomple te?

Page 45: [PyCon2016]To mock or not to mock, that is the questions
Page 46: [PyCon2016]To mock or not to mock, that is the questions

Integration tests

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

Page 47: [PyCon2016]To mock or not to mock, that is the questions

Unit tests

Integration tests

Mocks

Page 48: [PyCon2016]To mock or not to mock, that is the questions

Only mock types that you own

Page 49: [PyCon2016]To mock or not to mock, that is the questions

Building onThird-Party

Code

Page 50: [PyCon2016]To mock or not to mock, that is the questions

Adapter layer

3rd party API

Application objects

Page 51: [PyCon2016]To mock or not to mock, that is the questions

Adapter layer

3rd party API

Application objects

Test this

cluster

Mock this

Page 52: [PyCon2016]To mock or not to mock, that is the questions

Conclusions

Page 53: [PyCon2016]To mock or not to mock, that is the questions

Mocks can be

dangerous

Page 54: [PyCon2016]To mock or not to mock, that is the questions

Passing faulty tests give a fal se sense of security

Page 55: [PyCon2016]To mock or not to mock, that is the questions

The end