Date post: | 29-Dec-2015 |
Category: |
Documents |
Upload: | darren-day |
View: | 218 times |
Download: | 1 times |
Copyright © 2008 Jeppesen Sanderson, Inc. Jeppesen Proprietary and Confidential Slide #
Overview
• What is a “mock”?• What do “coded mocks” look like?• Introducing Capture-Replay mocks • CaptureMock, with examples• Demo
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.
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)
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);
Copyright © 2008 Jeppesen Sanderson, Inc.
Mocking in Python (Mock)
# Fix method return values and verify they were calledfrom 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())
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 testtime.asctime = hardcoded_asctime
class ProductionClass: def method(self): print ”The time is”, time.asctime()
real = ProductionClass()# Will print our faked, deterministic answerreal.method()
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...
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
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 testmocker.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:
Copyright © 2008 Jeppesen Sanderson, Inc.
CaptureMock recording
System System under under testtest
3rd 3rd party party
modulemodule
CaptureMockCaptureMock
Record traffic
Copyright © 2008 Jeppesen Sanderson, Inc.
CaptureMock replay
System System under under testtest
3rd 3rd party party
modulemoduleCaptureMockCaptureMock
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.
Copyright © 2008 Jeppesen Sanderson, Inc.
A Python CaptureMock test
# test_email.pyfrom 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”
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@localhostTo: tom,dick,harrySubject: Hi Guys!
I love you all!''')->RET:{}<-PYT:smtp1.quit()
When I run without CAPTUREMOCK_MODE, no email will be sentand if the calls received differ from above, an exception will be thrown.
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
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 thestandard output, standard error, exit code and files written.
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.
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
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