You are on page 1of 3

To Replay or to AAA?

Some project use the Mox mocking framework. It uses the Record/Replay paradigm for unit testing.
I've had a couple brief discussions about a contrasting style of mocking frameworks - called AAA
(Arrange, Act, Assert). A couple folks have asked me why I find Record/Reply less good. Last night
a simple example popped into my head. First the code we want to test
class Yeah:
"""It does a couple things"""
def __init__(self):
self.timeStamp = []
self.helper = Helper()
def DoSomething(self):
self.timeStamp = datetime.datetime.now()
return True
def DoMore(self):
self.helper.DoABitOfHelp();
now = datetime.datetime.now()
return self.timeStamp and now > self.timeStamp + datetime.timedelta(seconds=3600)

The logic of interest is that the DoMore() method won't return True until an hour after
DoSomething() is called.
Here's a simple little unit test for that, using Mox
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

def testDoingMoreAfterOneHour(self):
realNow = datetime.datetime.now()
nowPlusOneHour = realNow + datetime.timedelta(seconds=3601)
# need to control 'time'
self.m.StubOutWithMock(datetime, 'datetime')
# define how time is to behave
datetime.datetime.now().AndReturn(realNow)
datetime.datetime.now().AndReturn(nowPlusOneHour)
self.m.ReplayAll()
yeah = Yeah()
self.assertTrue(yeah.DoSomething())
self.assertTrue(yeah.DoMore())
self.m.VerifyAll()

When I look at the above I note a couple things. First, the "meat" of what is being tested is that
DoSomething() and DoMore() behave correctly. That is simple and represented by these 3
lines:
14
15
16

yeah = Yeah()
self.assertTrue(yeah.DoSomething())
self.assertTrue(yeah.DoMore())

Unfortunately nothing in this code snippet expresses that an hour of time has passed. To
understand that one has to look back in the "Record" section of the code to see these 3 lines:
8
9

# define how time is to behave


datetime.datetime.now().AndReturn(realNow)

10

datetime.datetime.now().AndReturn(realNow + datetime.timedelta(seconds=3601))

I don't find this style of mocking/testing very readable or expressive. Theres no connection
between the Record code and the Replay code. No clear representation of where the time
moving forward 1 hour is going to play a role in the test.
A second notable characteristic is that the test is intimately tied to the implementation of the
Yeah class (and all the code that Yeah uses). Suppose that DoABitOfHelp() method was
changed due to some unrelated work and it now calls datetime.now(). Here's DoMore()
again...
def DoMore(self):
self.helper.DoABitOfHelp();
# unbeknownst to me DoABitOfHelp() now calls datetime.now()
now = datetime.datetime.now()
return self.timeStamp and now > self.timeStamp + datetime.timedelta(seconds=3600)

What is going to happen to the unit test above? It will start to fail. It fails because the new/extra
call to datetime.now screws things up. IMO this style of unit testing (Record/Replay) lends
itself toward fragile unit tests, one's too dependent on the implementation.
Let's rewrite the unit test in a mythical AAA (Arrange, Act, Assert) style. There are AAA
frameworks for all the platforms, including python. I don't want to take any time to learn a real
python AAA framework today (see http://www.voidspace.org.uk/python/mock/) so I'm just going
to fabricate the syntax...
1 def
2
3
4
5
6
7
8
9
10
11

testDoingMoreAfterOneHour(self):
realNow = datetime.datetime.now()
nowPlusOneHour = realNow + datetime.timedelta(seconds=3601)
# need to control 'time'
self.mock.StubOut(datetime, 'datetime')
yeah = Yeah()
datetime.datetime.now().Returns(realNow)
# From here on now() will return 'realNow'
self.assertTrue(yeah.DoSomething())
datetime.datetime.now().Returns(nowPlusOneHour) # Now now() will return this new time
self.assertTrue(yeah.DoMore())

This model (IMO) is both more readable and more expressive. The key thing being tested is
clearly visible in the code - lines 9-10. The passing of 1 hour of time happens right before the
call to DoMore(). To me the Record/Replay model leads one to first work in a shadow world,
where you are forced to think through the scenario being tested from the "underside" or
"shadow". And once that is complete then go through that same scenario a second time, this
time for "real".
In discussing this with a fine colleague we came up with ideas on how to mimic the AAA style in
Mox. That might look something like this:
def testDoingMoreAfterOneHour2(self):
realNow = datetime.datetime.now()
nowPlusOneHour = realNow + datetime.timedelta(seconds=3601)
# need to control 'time'
self.m.StubOutWithMock(datetime, 'datetime')
# Set first time (do a Record/Replay cycle)
# Use MultipleTimes to avoid being tied to implementation

datetime.datetime.now().MultipleTimes(Optional=True).AndReturns(realNow)
self.m.ReplayAll()
yeah = Yeah()
self.assertTrue(yeah.DoSomething())
self.m.VerifyAll()
self.m.ResetAll()
# Set the second time (do a Record/Replay cycle)
datetime.datetime.now().MultipleTimes(Optional=True).AndReturns(nowPlusOneHour)
self.m.ReplayAll()
self.assertTrue(yeah.DoMore())
self.m.VerifyAll()

Something like this would work and helper functions / extensions to the framework could hide
some of this. I think that is the genesis of the AAA style of mocking frameworks. Record/Replay
came first. AAA was an evolution to create more robust, expressive, and readable tests.
Cheers!

You might also like