Download presentation
Presentation is loading. Please wait.
Published byDarren Day Modified over 9 years ago
1
Capture-Replay Mocks Scandinavian Developer Conference 4 th April 2011 Geoff Bache
2
Copyright © 2008 Jeppesen Sanderson, Inc.Jeppesen Proprietary and ConfidentialSlide # Overview What is a “mock”? What do “coded mocks” look like? Introducing Capture-Replay mocks CaptureMock, with examples Demo
3
Copyright © 2008 Jeppesen Sanderson, Inc. What are “mocks”? “Simulated objects that mimic the behaviour of real objects in controlled ways” (Wikipedia) More strictly: “mocks” actively expect and assert behaviour “stubs” passively return hardcoded values “mocks and stubs” are collectively called “fakes” or “test doubles”. Terminology unfortunately not used consistently.
4
Copyright © 2008 Jeppesen Sanderson, Inc. Examples of when to use mocks/stubs When using the real code in your test would be Hard to analyse (e.g. images created) Too slow (e.g. databases) Indeterministic (e.g. current time) Hard/expensive to set up (e.g. network errors) Impossible (because it doesn’t exist yet) As a design strategy (Behaviour-Driven Development)
5
Copyright © 2008 Jeppesen Sanderson, Inc. Mocking in Java (Mockito) //You can mock concrete classes, not only interfaces LinkedList mockedList = mock(LinkedList.class); //stubbing stub(mockedList.get(0)).toReturn("first"); stub(mockedList.get(1)).toThrow(new RuntimeException()); //following prints "first" System.out.println(mockedList.get(0)); //following throws runtime exception System.out.println(mockedList.get(1)); //following prints "null" because get(999) was not stubbed System.out.println(mockedList.get(999)); //Checks that we called “get(0)” at some point verify(mockedList).get(0);
6
Copyright © 2008 Jeppesen Sanderson, Inc. Mocking in Python (Mock) # Fix method return values and verify they were called from mock import Mock my_mock = Mock() my_mock.some_method.return_value = "value" assertEqual("value", my_mock.some_method()) my_mock.some_method.assert_called_once_with() # Set up expectation we will raise exceptions... my_mock.other_method.side_effect = SomeException("message") assertRaises(SomeException, my_mock.other_method())
7
Copyright © 2008 Jeppesen Sanderson, Inc. Stubbing in Python (“monkey patching”) # Example of ”monkey-patching” import time def hardcoded_asctime(): return 'Thu Mar 24 16:12:44 2011' # We monkey-patch the standard library for our test time.asctime = hardcoded_asctime class ProductionClass: def method(self): print ”The time is”, time.asctime() real = ProductionClass() # Will print our faked, deterministic answer real.method()
8
Copyright © 2008 Jeppesen Sanderson, Inc. Problems with using mocks Involves writing sometimes complex code Takes time to write Has to be maintained Easy to create a lot of dependency on implementation details If the “real code” changes its behaviour : The tests stay green But the system doesn’t work any more...
9
Copyright © 2008 Jeppesen Sanderson, Inc. Capture-Replay Mocks Generate mocks by running the real code and capturing how it behaves Results in a normal test that can be run in two ways using the stored mocks or regenerating them from the real code No mock-code to write, just say what you want to capture Much easier to keep in synch with real code
10
Copyright © 2008 Jeppesen Sanderson, Inc. It's not “capture-replay” like EasyMock! # Some mock frameworks just use “capture-replay” as a syntax import mocker # We start in the “capture phase”... my_mock = mocker.mock() my_mock.some_method() mocker.result("value") # and then we “switch to replay” when we're ready to test mocker.replay() #... test code import mock my_mock = mock.Mock() my_mock.some_method.return_value = "value" #... test code Which is basically just a more verbose way to express:
11
Copyright © 2008 Jeppesen Sanderson, Inc. CaptureMock recording System under test 3rd party module CaptureMock Record traffic
12
Copyright © 2008 Jeppesen Sanderson, Inc. CaptureMock replay System under test 3rd party module CaptureMock
13
Copyright © 2008 Jeppesen Sanderson, Inc. CaptureMock : doing it in practice CaptureMock is a tool written in Python that can do this with Python code (modules and attributes) Command-line calls (subprocesses) Plain-text messaging (over sockets) “Python code” part relies on Python’s dynamic features Other parts are language-independent Plain-text messaging assumes a synchronous model each socket sends one message and gets one reply.
14
Copyright © 2008 Jeppesen Sanderson, Inc. A Python CaptureMock test # test_email.py from capturemock import capturemock class ProductionClass: def method(self): # call some code resulting in email being sent self.something() @capturemock(”smtplib”) def test_email_sending(): real = ProductionClass() real.method() I can then run: $ env CAPTUREMOCK_MODE=1 test_email.py which will actually send the email and record the interaction with smtplib to a file called ”email_sending.mock”
15
Copyright © 2008 Jeppesen Sanderson, Inc. A CaptureMock recorded mock (Python) <-PYT:import smtplib <-PYT:smtplib.SMTP() ->RET:Instance('SMTP', 'smtp1') <-PYT:smtp1.connect('machine.site.com') ->RET:(220, 'machine.site.com ESMTP Sendmail; Tue, 9 Feb 2010 14:32:54 +0100') <-PYT:smtp1.sendmail('me@localhost', ['tom', 'dick', 'harry'], '''From: me@localhost To: tom,dick,harry Subject: Hi Guys! I love you all! ''') ->RET:{} <-PYT:smtp1.quit() When I run without CAPTUREMOCK_MODE, no email will be sent and if the calls received differ from above, an exception will be thrown.
16
Copyright © 2008 Jeppesen Sanderson, Inc. A command-line CaptureMock test # update_and_build.sh # We update some code from CVS and then try to build it returned=`cvs update –dP /path/to/my/checkout` # logic using stuff written on stdout etc... make Here I put the following in.capturemockrc to tell it to capture calls to CVS [command line] intercepts = cvs which will perform the update for real and record to ”cvs_calls.mock” $ capturemock --record cvs_calls.mock update_and_build.sh and then run
17
Copyright © 2008 Jeppesen Sanderson, Inc. A recorded mock from a subprocess <-CMD:cvs update -dP /path/to/my/checkout ->FIL:checkout ->OUT:U subdir/myfile.txt ->ERR:cvs update: Updating. cvs update: Updating subdir Note the ”FIL” line refers to stored copies of the files changed by cvs. They are stored in a directory called ”cvs_calls.files”. To replay this, we run $ capturemock --replay cvs_calls.mock update_and_build.sh maybe on a machine where CVS isn't installed. CaptureMock will pretend to be CVS, reproducing the standard output, standard error, exit code and files written.
18
Copyright © 2008 Jeppesen Sanderson, Inc. A client-server CaptureMock test Assuming I have a client that sends strings to a server, which replies with how long they are, and a script that runs both together: $ capturemock --record communication.mock run_client_server.sh <-CLI:Here is a string ->SRV:Length was 16 <-CLI:Here is a longer string ->SRV:Length was 23 Something like this will appear in communication.mock: I can then use this data to test either the client or the server in isolation from the other.
19
Copyright © 2008 Jeppesen Sanderson, Inc. Demo : graphs and matplotlib
20
Copyright © 2008 Jeppesen Sanderson, Inc. Examples of when to use CaptureMock When using the real code in your test would be Hard to analyse - yes Too slow - yes Indeterministic - yes, with care (record mode may fail) Hard to set up - possibly (record mode still hard to set up) Impossible because it doesn’t exist yet - no As a design strategy (Behaviour-Driven Development) - no
21
Copyright © 2008 Jeppesen Sanderson, Inc. Conclusions Existing mocking approaches – are labour-intensive when non-trivial code is mocked out – don't provide an easy means to verify the mock against real code CaptureMock – provides a means to (re-)generate mock information from the code – and two "modes" to select from each time tests are run
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.