Upgrade to Pro — share decks privately, control downloads, hide ads and more …

[DjangoCon Europe 2016] To mock or not to mock, that is the question

[DjangoCon Europe 2016] To mock or not to mock, that is the question

Mocking is a very powerful testing concept that has some dangerous pitfalls. There are obvious use cases where mocks are an absolute requirement to be able to test a part of the app. Nevertheless sometimes apparently useful mocks can yield erroneous test results. This talk goes into deeper detail on the trade-off of using mocks in testing.

Ana Balica

March 30, 2016
Tweet

More Decks by Ana Balica

Other Decks in Programming

Transcript

  1. Mock >>> from mock import Mock >>> m = Mock()

    >>> m.foo = 1 >>> m.foo 1 >>> m.bar <Mock name='mock.bar' id='4310136016'>
  2. HTTP requests /responses requests.get with mock.patch("foo.requests.get") as mock_get: mock_get.return_value =

    mock.Mock(status_code=200) status = is_it_down(“http://djangocon.eu") self.assertFalse(status)
  3. IO operations m = mock.mock_open(read_data="DjangoCon \o/“) with mock.patch("foo.open", m, create=True):

    with open("bar.txt") as h: result = h.read() self.assertEqual(result, "DjangoCon \o/") open
  4. Clocks, time, timezones from django.utils import timezone tz = pytz.timezone('Asia/Tokyo')

    dt = datetime.datetime(2016, 1, 1, tzinfo=tz) with mock.patch.object(timezone, 'now', return_value=dt): print timezone.now() timezone.now
  5. ✓ System calls ✓ Streams ✓ HTTP requests ✓ IO

    operations ✓ Clocks, time, timezones ✓ Unpredictable results
  6. Why we like them ✓ Save time ✓ Make impossible

    possible ✓ Exclude external dependencies
  7. with mock.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?
  8. with mock.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?
  9. Problems? with mock.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()
  10. Problems? with mock.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()
  11. Problems? with mock.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()
  12. Problems? with mock.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_a_sandwich()
  13. Solution 1 with mock.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()
  14. Solution 2 with mock.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)
  15. Failure with mock.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)
  16. Problems? with mock.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)