CF EasyMock port

6 views
Skip to first unread message

M Steele

unread,
Mar 20, 2008, 3:22:37 PM3/20/08
to mxu...@googlegroups.com

All,

 

Some of you have expressed interest in taking a look at the CF port of EasyMock I've been working on.  Well, the first pass is done, so if you could reply off-list I'll get zips with docs out to you.

 

I'll try and get the source up on a publicly available site once, I've completely finished.

 

Mike Steele



Never miss a thing. Make Yahoo your homepage.

Sean Corfield

unread,
Mar 20, 2008, 5:17:14 PM3/20/08
to mxu...@googlegroups.com
On Thu, Mar 20, 2008 at 12:22 PM, M Steele <m_ste...@yahoo.com> wrote:
> Some of you have expressed interest in taking a look at the CF port of
> EasyMock I've been working on. Well, the first pass is done, so if you
> could reply off-list I'll get zips with docs out to you.

Sounds like Brian Kotek's ColdMock?

Can you give more detail about what EasyMock provides that ColdMock does not?
--
Sean A Corfield -- (904) 302-SEAN
An Architect's View -- http://corfield.org/

"If you're not annoying somebody, you're not really alive."
-- Margaret Atwood

M Steele

unread,
Mar 20, 2008, 6:31:57 PM3/20/08
to mxu...@googlegroups.com
It allows for behavioral checking of the function/component under test.  That is to say, it is more than just a stub function with dummy return.  You can record expected behavior for your mock, and then verify expected behavior against actual behavior to see if the function under test is actually doing what is expected.  Most unit tests rely on a return value to verify assertion, but if there is no return value, checking becomes more difficult.
 
An example of this, (granted extremely simple, and probably not the best off the cuff) follows.  If you just create a simple mock of the listener required on init(), how do you know if the length condition was actually met when newDocument() is called?
 

<cfcomponent

name="Document">

    <cffunction name="init">

        <cfargument name="listener">

        <cfset variables.listener = arguments.listener />

        <cfreturn this />

    </cffunction>

 

    <cffunction name="newDocument">

        <cfargument name="docName">

        <cfif len( arguments.docName ) gt 4>

            <cfset variables.listener.createDocument( arguments.docName ) />

        </cfif>

    </cffunction>

</cfcomponent>

 
Then to test both outcomes of the if condition, you can use EasyMock to define behavior for both.
 
<cfset listenerMock = createObject( "component", "MockFactory" ).createMock( "listener" ) />
<cfset listenerMock.createDocument( "MyDocument" ) />  <!--- This specifies the expected behavior for this call, that createDocument() will only be called once.--->
<cfset listenerMock.getProxyInvoker().replay() />  <!--- Now switch from taking expected calls, to actual calls --->
 
<cfset doc = createObject( "component", "Document" ).init( listenerMock ) />
<cfset doc.newDocument( "MyDocument" ) />
<cfset listenerMock.getProxyInvoker().verify() />  <!--- This will evaluate the expected behavior above, with the actual behavior from the proceeding line.
 
This is a very basic example, but when you have a larger number of collaborators it becomes much more useful in making sure that everything inside the function under test is behaving as expected.

Looking for last minute shopping deals? Find them fast with Yahoo! Search.

M Steele

unread,
Mar 20, 2008, 6:40:28 PM3/20/08
to mxu...@googlegroups.com
Also, CF8 isn't a requirement.  I ported some of the java.reflect package to CF to support the port of EasyMock

M Steele

unread,
Mar 20, 2008, 6:52:38 PM3/20/08
to mxu...@googlegroups.com
You can also, create mocks without any behavior checking, so the stub style mock is still available in EasyMock.

billy

unread,
Mar 20, 2008, 8:54:09 PM3/20/08
to mxunit
Martin Fowler has a piece I found very helpful and makes a clear
distinction between state-based and behavioral-based verification. As
Mike stated, with a mock, you are not necessarily testing the state of
the CUT (which sounds better than than SUT), but testing that the CUT
behaved as expected, or made the correct calls on the mock. The
ability to replay and verify that interaction with collaborators is
very interesting to me, despite my naive assert(someState) mentality.

The point Fowler makes in the end seems to be one of preference -
there are classicists (state-based) and mockists (behavioral-based).
As far as I'm concerned, if you're writing _any_ unit tests in CF, you
should be rewarded. As a matter of fact, it's a distinct possibility
Marc Esher will buy you a Cohiba or some Dalwhinnie if you catch him
in the right mood.

http://martinfowler.com/articles/mocksArentStubs.html

bill

On Mar 20, 5:52 pm, M Steele <m_steel...@yahoo.com> wrote:
> You can also, create mocks without any behavior checking, so the stub style mock is still available in EasyMock.
>
> ----- Original Message ----
> From: M Steele <m_steel...@yahoo.com>
> To: mxu...@googlegroups.com
> Sent: Thursday, March 20, 2008 3:40:28 PM
> Subject: [mxunit:149] Re: CF EasyMock port
>
> Also, CF8 isn't a requirement. I ported some of the java.reflect package to CF to support the port of EasyMock
>
> ----- Original Message ----
> From: Sean Corfield <seancorfi...@gmail.com>
> To: mxu...@googlegroups.com
> Sent: Thursday, March 20, 2008 2:17:14 PM
> Subject: [mxunit:147] Re: CF EasyMock port
>
> On Thu, Mar 20, 2008 at 12:22 PM, M Steele <m_steel...@yahoo.com> wrote:
> > Some of you have expressed interest in taking a look at the CF port of
> > EasyMock I've been working on. Well, the first pass is done, so if you
> > could reply off-list I'll get zips with docs out to you.
>
> Sounds like Brian Kotek's ColdMock?
>
> Can you give more detail about what EasyMock provides that ColdMock does not?
> --
> Sean A Corfield -- (904) 302-SEAN
> An Architect's View --http://corfield.org/
>
> "If you're not annoying somebody, you're not really alive."
> -- Margaret Atwood
>
> Looking for last minute shopping deals? Find them fast with Yahoo! Search.
>
> ____________________________________________________________________________________
> Never miss a thing. Make Yahoo your home page.http://www.yahoo.com/r/hs

Brian Kotek

unread,
Mar 20, 2008, 8:55:01 PM3/20/08
to mxu...@googlegroups.com
If I read this correctly, you're trying to verify whether, when
newDocument() is called, if the listener actually creates a new
document after the length check? And because newDocument() doesn't
return anything that can be used to check, and because
createDocument() doesn't appear to do anything that can be checked
either, there is no easy way to know if createDocument() did, in fact
run?

I realize that you're trying to give a simple use case, but this seems
quite contrived. Do you have a more "real world" example? Because I
just don't see something like this actually happening in a
application, where a method call is simply swallowed up with no
indication of whether an expected result actually happened. Let me put
it this way: if the above Document component was used in a real
application, and something called newDocument() on it, how would the
actual application know whether anything happened or not?

In any event, it would be pretty easy for me to add a
"methodCallCount()" to the mock object so that you could tell how many
times a given method was run, if people think that would help.

Brian Kotek

unread,
Mar 21, 2008, 11:28:47 AM3/21/08
to mxu...@googlegroups.com
Since this was a simple thing to add I just added it and updated the
ColdMock distro and SVN repo at RIAForge. You can now call
myMock.methodCallCount('someMockedMethod') to determine how many times
that method has been run so far.

billy

unread,
Mar 21, 2008, 12:00:29 PM3/21/08
to mxunit
'Once,' said the Mock Turtle at last, with a deep sigh, 'I was a real
Turtle.' -Alice in Wonderland

It's awesome to have mocks available for CF! Nice job!

bill

On Mar 21, 10:28 am, "Brian Kotek" <brian...@gmail.com> wrote:
> Since this was a simple thing to add I just added it and updated the
> ColdMock distro and SVN repo at RIAForge. You can now call
> myMock.methodCallCount('someMockedMethod') to determine how many times
> that method has been run so far.
>
> On Thu, Mar 20, 2008 at 8:55 PM, Brian Kotek <brian...@gmail.com> wrote:
> > If I read this correctly, you're trying to verify whether, when
> > newDocument() is called, if the listener actually creates a new
> > document after the length check? And because newDocument() doesn't
> > return anything that can be used to check, and because
> > createDocument() doesn't appear to do anything that can be checked
> > either, there is no easy way to know if createDocument() did, in fact
> > run?
>
> > I realize that you're trying to give a simple use case, but this seems
> > quite contrived. Do you have a more "real world" example? Because I
> > just don't see something like this actually happening in a
> > application, where a method call is simply swallowed up with no
> > indication of whether an expected result actually happened. Let me put
> > it this way: if the above Document component was used in a real
> > application, and something called newDocument() on it, how would the
> > actual application know whether anything happened or not?
>
> > In any event, it would be pretty easy for me to add a
> > "methodCallCount()" to the mock object so that you could tell how many
> > times a given method was run, if people think that would help.
>
> > > On Thu, Mar 20, 2008 at 12:22 PM, M Steele <m_steel...@yahoo.com> wrote:
> > > > Some of you have expressed interest in taking a look at the CF port of
> > > > EasyMock I've been working on. Well, the first pass is done, so if you
> > > > could reply off-list I'll get zips with docs out to you.
>
> > > Sounds like Brian Kotek's ColdMock?
>
> > > Can you give more detail about what EasyMock provides that ColdMock does
> > > not?
> > > --
> > > Sean A Corfield -- (904) 302-SEAN
> > > An Architect's View --http://corfield.org/

Sean Corfield

unread,
Mar 24, 2008, 1:02:48 AM3/24/08
to mxu...@googlegroups.com
On Thu, Mar 20, 2008 at 3:31 PM, M Steele <m_ste...@yahoo.com> wrote:
> That is to say, it is more than just a stub function with dummy return. You
> can record expected behavior for your mock, and then verify expected
> behavior against actual behavior to see if the function under test is
> actually doing what is expected. Most unit tests rely on a return value to
> verify assertion, but if there is no return value, checking becomes more
> difficult.

True, you have to find other invariants to assert.

> Then to test both outcomes of the if condition, you can use EasyMock to
> define behavior for both.

You show how to test for the call happening - how would you verify the
call did not happen? Would it just be an empty sequence of calls? I
think, as Brian hinted, this really just speaks to the number of
calls. Can you give a bit more detail? I'm intrigued but not convinced
yet.

M Steele

unread,
Mar 24, 2008, 10:33:34 PM3/24/08
to mxu...@googlegroups.com

I apologize for not having a more 'real' example to show you at this moment.  Work and personal issues are eating up a lot of my time.

 

Behavior checking requires that you 1) record the expected behavior, 2) test the method, 3) verify the expected behavior and the actual behavior match.  So you are correct that just giving a number of method calls isn't enough.  The EasyMock package provides the verify() method, which will compare all recorded actual calls with the expected calls.

 

<!--- Set the expectation, mocks in this mock framework start in record mode.  so any method call made on the mock is recorded --->

<cfset mock.getProxyInvoker().expect( mock.someFunction() ).andReturns( "Hello" ) />

 

<!--- Now switch the mock to accept the actual method calls --->

<cfset mock.getProxyInvoker().replay() />

 

<!--- We expect that testFunction will call mockSomeFunction() with no arguments.  FYI, testFunction doesn't return a value --->

<cfset testComp.testFunction( mock ) />

 
<!--- Now verify that expected and actual match.  If they don't then an error is generated stating what didn't match --->
<cfset mock.getProxyInvoker().verify() />
 
One of the issues we started running into, was that the developers were returning values from methods, just so a test assertion could be made.  Or they would code getters into the components, with a sole purpose to provide introspection for a unit test.
 
I'll have a more concrete example after this week (which is our release, which unfortunately doesn't pertain to this Mock project).  For the most part, just being able to mock an object, and run valid tests covers a large percentage of what is needed for CF unit tests.  If all you are doing is writing functions that take a given input and give an expected output, without any/minimal collaboration with other obejcts then behavior validation isn't much use.  If your design requires more collaboration, and you find yourself writing extra methods just to support your tests, then you may want to check out behavior mocking.
 
Y

Sean Corfield

unread,
Mar 27, 2008, 1:56:20 AM3/27/08
to mxu...@googlegroups.com
On Mon, Mar 24, 2008 at 7:33 PM, M Steele <m_ste...@yahoo.com> wrote:
> Behavior checking requires that you 1) record the expected behavior, 2) test
> the method, 3) verify the expected behavior and the actual behavior match.
> So you are correct that just giving a number of method calls isn't enough.
> The EasyMock package provides the verify() method, which will compare all
> recorded actual calls with the expected calls.
>
> <!--- Set the expectation, mocks in this mock framework start in record
> mode. so any method call made on the mock is recorded --->
>
> <cfset mock.getProxyInvoker().expect( mock.someFunction() ).andReturns(
> "Hello" ) />
>
> <!--- Now switch the mock to accept the actual method calls --->
>
> <cfset mock.getProxyInvoker().replay() />
>
> <!--- We expect that testFunction will call mockSomeFunction() with no
> arguments. FYI, testFunction doesn't return a value --->
>
> <cfset testComp.testFunction( mock ) />
>
> <!--- Now verify that expected and actual match. If they don't then an
> error is generated stating what didn't match --->
> <cfset mock.getProxyInvoker().verify() />

OK, that helps me understand better. Interesting. Thanx.

Reply all
Reply to author
Forward
0 new messages