Prior to version 2.0, expected and actual behaviour was determined by performing equality checks between the method signatures of the expected calls and the actual calls. All function parameters were checked for direct equality. (Objects/components were the exception and were just checked for type.)
Setting expected behaviours would work fine as long as the method under test doesn't pass internally generated data, such as a date or a UUID, into the mock.
As you can see, this behaviour can be limiting.
<cffunction name="setCacheDate" />
<cfargument name="cacheItem" required="yes" type="CacheableItem">
<cfset arguments.cachedItem.setCacheDate( now() ) />
<!--- add item to cache, etc. --->
Now we attempt to write a unit test for the CacheManager.
<cfset mf = createObject( "component", "easymock.MockFactory" ) />
<cfset ciMock = mf.createMock( "myItem" ) />
<cfset mgr = createObject( "component", "CacheManager" ) />
<cfset mf.expect( ciMock.setCacheDate( now() ) ) />
<cfset mf.replay( ciMock ) />
<cfset mgr.add( ciMock ) />
<cfset mf.verify( ciMock ) />
Everything looks like it's in order. We're calling the setCacheDate() function and passing it the same value as in the CacheManager. However, when this test runs it will fail (unless your computer is infinitely fast). Why? Because the timestamp value returned from now() is not the same as the value passed when the test calls the add() function. So how do you tell the mock to expect something that "matches" a given input, but doesn't have to "equal" that input?
This is where matchers come in to play. Version 2.0 introduces matchers as a way to manage expected parameters when a specific data match is not necessary or impossible to reproduce.
In the above example, we would change the expect() call on the ciMock to use one of the matchers provided by CFEeasyMock
<cfset mf.expect( ciMock.setCacheDate( mf.after( '1900-01-01' ) ) ) />
The mf.after() function call returns a DateMatcher that will return true for any value on or after January 1, 1900. If no matcher is specified, the value passed to the mock function call during record phase is wrapped in a matcher that matches based on equal values. So the following two expectations are identical to CFEasyMock in record mode:
<cfset mf.expect( mock.setName( mf.eqs( "Rumplestiltskin" ) ) ) />
For a complete list of matchers see the Appendix. You can also create your own matchers by implementing the IMatcher interface. There are three methods that the interface requires to be implemented.
matches( any ) This function is called to determine if the matcher's instance value matches the value passed.
asString() This function is called to display only the matcher parameter as a string. The simplest return string would be the matcher's instance value. Other matchers have more complex return strings, as in the case of the DateMatcher and NumericMatcher.
isEqual( IMatcher ) This function is called to determine if two matchers are equal. The function should return whether or not two matchers are equal based on matcher component type as well as the matcher instance values.