tag:blogger.com,1999:blog-27623128045376541462024-03-05T22:56:43.518+00:00Super Duper CodeAll about developing on the Salesforce platform.
Mostly Apex and Visualforce, but with occasional forays into Java and Javascript when the mood takes me.Anonymoushttp://www.blogger.com/profile/05661077803935743665noreply@blogger.comBlogger2125tag:blogger.com,1999:blog-2762312804537654146.post-66138457064749142262016-06-23T09:45:00.000+01:002016-06-23T10:08:07.197+01:00ApexMocks Capture Support<h2>
Capture Support</h2>
<div>
Today's post focuses on the new Capture feature in ApexMocks. This is an awesome contribution from my talented FinancialForce colleague Vincenzo Denti. This is another feature ported over from <a href="http://mockito.github.io/mockito/docs/current/org/mockito/Captor.html">Mockito</a>, filling out our ever growing unit testing toolbelt.</div>
<div>
<br /></div>
<div>
All code samples from this post can be found <a href="http://github.com/frup42/capture_201606">in this Github repo</a>.</div>
<div>
<br /></div>
<div>
ApexMocks already allowed you to verify that a method was called a certain number of times, with a specific set of arguments.<br />
<br />
Say you want to inspect one of those arguments to verify its state. You need to get a hold of the argument. In many cases, you supply the argument into the method under test, or you can stub the argument as a return value for a method.<br />
<br />
Equally, there are instances when you don't have a reference to the method, perhaps because it is created inside the method under test. Maybe it generates a random token so you can't predict the value, or it mutates the object so that it is no longer <b>equal</b> to the Object you had a reference to.<br />
<br />
Capturing exposes argument values for a particular method call, allowing you to:<br />
<br />
<ol>
<li>Perform further asserts.</li>
<li>(Bonus) dump the argument while you write your unit test - making it easier to declare the expected argument values or debug failing tests.</li>
</ol>
<br />
Let's see this in action!<br />
<br /></div>
<h3>
Example code</h3>
Let's construct a contrived sample application. The application is inspired by that classic disco tune "Get Down Saturday Night". You can thank me later for getting the song stuck in your head.<br />
<br />
I've built this on top of ApexMocks and ApexCommon, so if you want to deploy this into an org you will have to deploy those first:<br />
<br />
<ol>
<li><a href="http://github.com/financialforcedev/fflib-apex-mocks">ApexMocks</a></li>
<li><a href="http://github.com/financialforcedev/fflib-apex-common">ApexCommon</a></li>
</ol>
<br />
<br />
Let's take a look at the AwesomeService. This class has a dependency on the DiscoService class, which is injected using the Application factory pattern (discussed at length <a href="https://andyinthecloud.com/2014/08/26/preview-of-advanced-apex-enterprise-patterns-session/">here</a>). Given a user and a date, AwesomeService will either ask the DiscoService to assist the user in 'getting down' or notify the user by creating a DiscoService.Event.<br />
<br />
<b>AwesomeService</b><br />
<pre class="prettyprint lang-java linenums">public with sharing class AwesomeService
{
//Don't match to 'Sat' - instead format a known Saturday, to avoid localisation issues
private static final String FMT = 'EEE';
private static final String SATURDAY = Datetime.newInstance(1970, 1, 3).format(FMT);
private static IDiscoService disco = (IDiscoService)Application.Service.newInstance(IDiscoService.class);
public static void getDownSaturdayNight(User u, Datetime now)
{
if (now.format(FMT) == SATURDAY
&& u.LikesToParty__c //(everybody does)
&& u.CantWaitForTheWeekend__c)
{
disco.getDown(u);
}
else
{
DiscoService.Event event = new DiscoService.Event();
event.EventTarget = u;
event.EventType = 'Uncool';
event.Message = 'It\'s hip to be a square';
disco.notifyUser(event);
}
}
}</pre>
<br />
<div>
<b>IDiscoService</b></div>
<pre class="prettyprint lang-java linenums">public interface IDiscoService
{
void getDown(User u);
void notifyUser(DiscoService.Event event);
}</pre>
<div>
<br /></div>
<div>
<b>DiscoService.Event</b></div>
<pre class="prettyprint lang-java linenums">public class Event
{
public User EventTarget;
public String EventType;
public String Message;
}</pre>
<br />
<h3>
Unit Tests</h3>
<div>
<b>Unit testing the behaviour where the User is up for a party.</b></div>
<div>
This is a simple enough case.</div>
<br />
<pre class="prettyprint lang-java linenums">private static final DateTime KNOWN_SATURDAY = Datetime.newInstance(2000, 1, 1);
@isTest
private static void getDownSaturdayNight_getsDown_IfAppropriate()
{
fflib_ApexMocks mocks = new fflib_ApexMocks();
IDiscoService mockDisco = new Mocks.DiscoService(mocks);
Application.Service.setMock(IDiscoService.class, mockDisco);
//Given
User u = new User(
FirstName = 'Duff',
LastName = 'Man',
LikesToParty__c = true,
CantWaitForTheWeekend__c = true
);
//When
AwesomeService.getDownSaturdayNight(u, KNOWN_SATURDAY);
//Then
((IDiscoService)mocks.verify(mockDisco)).getDown(u);
}
</pre>
<div>
<b><br /></b>
<b>Unit testing the behaviour where the User refuses to party</b></div>
<div>
<div>
<pre class="prettyprint lang-java linenums">private static final DateTime KNOWN_SUNDAY = KNOWN_SATURDAY.addDays(1);
@isTest
private static void getDownSaturdayNight_doesntGetDown_OnASchoolNight_WithMatchers()
{
fflib_ApexMocks mocks = new fflib_ApexMocks();
IDiscoService mockDisco = new Mocks.DiscoService(mocks);
Application.Service.setMock(IDiscoService.class, mockDisco);
//Given
User u = new User(
FirstName = 'Buzz',
LastName = 'Killington',
LikesToParty__c = false,
CantWaitForTheWeekend__c = false
);
//When
AwesomeService.getDownSaturdayNight(u, KNOWN_SUNDAY);
//Then
((IDiscoService)mocks.verify(mockDisco)).notifyUser(matchesEvent( ??? )); //How do we verify the behaviour??
}
</pre>
<br /></div>
<div>
<b>Captors to the rescue</b><br />
We create an ArgumentCaptor, and supply it to the verify method. This brings the actual argument value into scope, allowing us to assert the state is as we expect it to be.<br />
<br /></div>
<div>
<pre class="prettyprint lang-java linenums">//Then
fflib_ArgumentCaptor argument = fflib_ArgumentCaptor.forClass(DiscoService.Event.class);
((IDiscoService)mocks.verify(mockDisco)).notifyUser((DiscoService.Event)argument.capture());
DiscoService.Event actual = (DiscoService.Event)argument.getValue();
System.assertEquals(actual.EventTarget, u);
System.assertEquals(actual.EventType, 'Uncool');
System.assertEquals(actual.Message, 'It\'s hip to be a square');</pre>
<div>
<br />
<h3>
Alternatives</h3>
<div>
There are of course other ways to test this code.</div>
<ul>
<li>Override equals/hashcode on the DiscoService.Event class - <a href="https://github.com/frup42/capture_201606/blob/master/src/classes/DiscoService.cls#L22">Example</a>.</li>
<ul>
<li>This approach relies on the vanilla ApexMocks matching behaviour - argument values are considered equal if their <b>equals</b> method says they are.</li>
<li>We leverage this by overriding equals and hashcodes on the DiscoService.Event class. </li>
<li>This means we have to write and maintain extra production code for the benefit of test classes, which feels inherently wrong. We have to ensure that these methods work accurately as well, in case non-test code comes to rely on this behaviour.</li>
</ul>
<li>Create a custom matcher - <a href="https://github.com/frup42/capture_201606/blob/master/src/classes/AwesomeServiceTest.cls#L85">Example</a>.</li>
<ul>
<li>We can define an fflib_IMatcher instance, in which we can provide our own definition of whether or not argument values should be considered equal.</li>
<li>The trouble with this approach is that it is very verbose. It takes about 50 lines of code to achieve the same thing that captors achieved in 5 lines.</li>
</ul>
</ul>
</div>
<h3>
Conclusions</h3>
<div>
Captors are another tool in your arsenal of testing strategies. They can't be used to stub method return values, and in some situations it may be more appropriate to use matchers for behaviour verification (e.g. where a common matcher already exists).<br />
<br />
But hopefully you can see the advantages they offer, and consider using them in your tests in the future.</div>
</div>
</div>
Anonymoushttp://www.blogger.com/profile/05661077803935743665noreply@blogger.com4tag:blogger.com,1999:blog-2762312804537654146.post-58570267617606103702016-03-13T20:44:00.001+00:002016-03-14T09:00:57.319+00:00Apex Mocks MatchersUnit testing in Apex can be tough. But ApexMocks makes your tests faster to write, and faster to run.<br />
<br />
I love ApexMocks, and it's more than likely they'll pop up in my future blog posts. If you want the low down on ApexMocks, start with the public <a href="https://github.com/financialforcedev/fflib-apex-mocks" target="_blank">ApexMocks GitHub repo</a> or check out the talk I gave at <a href="https://www.youtube.com/watch?v=6q5NkFZrX1o" target="_blank">London's Calling 2016</a>.<br />
<br />
Recently, ApexMocks had a substantial upgrade with the addition of matchers. But it didn't come with much documentation... leaving people to fumble through and fend for themselves under the shadowy veil of uncertain uncertainty. So for my very first foray into the world of blogging, let's sort that out!<br />
<br />
<h1>
<span style="font-weight: normal;">Introduction</span></h1>
<span style="font-weight: normal;"><b>
Old school mocks</b></span><br />
Traditionally, apex mocks has matched method calls to method counts and return values by comparing actual method argument values with expected method argument values.<br />
<br />
Apex mocks stored the lists of expected argument values internally in a Map. As a consequence, ApexMocks implicitly matched arg values using hashcode/equals methods defined on the arg value classes.<br />
<br />
This has several limitations:<br />
<br />
<ul>
<li>If you add an SObject record as the key in a map, and then change any of its field values, you will no longer be able to get it back out of the map. This post by Stephen Willcock goes into more detail: <a href="http://foobarforce.com/2013/09/10/sobject-secret-life-equality-sets-maps/">http://foobarforce.com/2013/09/10/sobject-secret-life-equality-sets-maps/</a></li>
<li>Lists, Maps and Sets all suffer from this problem too.</li>
<li>If you want to use custom classes as method arguments, you have to ensure there are correct equals and hashcode implementations for that class. This may mean implementing your own equals/hashcode methods in production code, only for the benefit of tests, which is a code smell.</li>
<li>If you want to stub a return value for a range of argument values, you need to add a stub for each argument permutation.</li>
<li>You cannot verify a method has been called with a range of argument values. E.g. if I have a method called doStuff(Integer x), I cannot verify it was called with any Integer between 1 and 10. The best I can do is verify it was called with a specific Integer value.</li>
</ul>
<ul>
</ul>
<span style="font-weight: normal;"><b>
<br />
Matchers</b></span><br />
Matchers allow you to invoke custom matching logic for method argument values.<br />
For example, you may want to stub a method when it is called with a specific SObject. This would fail using the concrete SObject record if its field values are changed before the method is called.<br />
<br />
Using matchers, you can instead stub a method when it is called with a specific SObject reference (i.e. using === rather than == in argument matching). Alternatively, you can match an sobject record with a given ID or name.<br />
<br />
Matchers offer more flexibility in defining when a method argument is a match. Even better, you can chain matchers, combine matchers and implement your own.<br />
<br />
<h1>
<span style="font-weight: normal;">
Verifying</span></h1>
Use this to verify a method has been called with a given set of arguments N times.<br />
<br />
<pre class="prettyprint lang-java linenums">fflib_ApexMocks mocks = new fflib_ApexMocks();
fflib_MyList.IList mockList = new fflib_Mocks.Mockfflib_MyList(mocks);
mockList.get(2);
mockList.get(4);
//Old school mocks
((fflib_MyList.IList) mocks.verify(mockList, 1)).get(2);
//Using integerBetween matcher
((fflib_MyList.IList) mocks.verify(mockList, 2)).get(fflib_Match.integerBetween(1, 10));</pre>
<br />
<h1>
<span style="font-weight: normal;">
Stubbing</span></h1>
Use this to return a specific value when a method is called with a given set of arguments.<br />
<br />
<pre class="prettyprint lang-java linenums">fflib_ApexMocks mocks = new fflib_ApexMocks();
fflib_MyList.IList mockList = new fflib_Mocks.Mockfflib_MyList(mocks);
mocks.startStubbing();
//Old school mocks
mocks.when(mockList.get(1)).thenReturn('One');
//Using integerMoreThan Matcher
mocks.when(mockList.get(fflib_Match.integerMoreThan(3))).thenReturn('>3');
mocks.stopStubbing();
System.assertEquals('One', mockList.get(1));
System.assertEquals('>3', mockList.get(1337));</pre>
<br />
<h1>
<span style="font-weight: normal;">
Throw exceptions</span></h1>
If you want to throw an exception from a non-void method, just stub the method as above but use an exception as the return value.<br />
<br />
<pre class="prettyprint lang-java linenums">fflib_ApexMocks mocks = new fflib_ApexMocks();
fflib_MyList.IList mockList = new fflib_Mocks.Mockfflib_MyList(mocks);
MyException ce = new MyException('Concrete exception'));
MyException me = new MyException('Matcher exception'));
mocks.startStubbing();
//Old school mocks
mocks.when(mockList.get(1)).thenReturn(ce);
//Using integerMoreThan Matcher
mocks.when(mockList.get(fflib_Match.integerMoreThan(5))).thenReturn(me);
mocks.stopStubbing();
try
{
mockList.get(1);
System.assert(false, 'Expected exception');
}
catch (MyException e)
{
System.assertEquals(e.getMessage(), 'Concrete exception');
}
try
{
mockList.get(23931);
System.assert(false, 'Expected exception');
}
catch (MyException e)
{
System.assertEquals(e.getMessage(), 'Matcher exception');
}</pre>
<br />
<br />
If you want to throw an exception from a void method, the syntax is similar to old school mocks.<br />
<br />
<pre class="prettyprint lang-java linenums">fflib_ApexMocks mocks = new fflib_ApexMocks();
fflib_MyList.IList mockList = new fflib_Mocks.Mockfflib_MyList(mocks);
MyException ce = new MyException('Concrete exception'));
MyException me = new MyException('Matcher exception'));
mocks.startStubbing();
//Old school mocks
((fflib_MyList.IList) mocks.doThrowWhen(ce), mockList)).add('concrete');
//Using stringContains Matcher
((fflib_MyList.IList) mocks.doThrowWhen(me), mockList)).add(fflib_Match.stringContains('matchers'));
mocks.stopStubbing();
try
{
mockList.add('concrete');
System.assert(false, 'Expected exception');
}
catch (MyException e)
{
System.assertEquals(e.getMessage(), 'Concrete exception');
}
try
{
mockList.add('matchers are good');
System.assert(false, 'Expected exception');
}
catch (MyException e)
{
System.assertEquals(e.getMessage(), 'Matcher exception');
}</pre>
<br />
<h1>
<span style="font-weight: normal;">
Combined matchers</span></h1>
Use this when you want to combine matchers. This can allow you to create ‘compound’ matchers, rather than proliferating matchers.<br />
For example, anyInteger would not match to ‘null.’<br />
If you want to match to anyInteger or null, you can combine the matchers using fflib_Match.anyOf(..) - instead of creating an anyIntegerOrNull custom matcher.<br />
<br />
To invoke a combined matcher, call the combined matcher helper, passing in further matchers as method args (see the code sample below)<br />
Combined matcher helpers<br />
<br />
<br />
<ul>
<li>fflib_Match.allOf(..)</li>
<ul>
<li>Match if the arg value matches ALL internal matchers.</li>
</ul>
<li>fflib_Match.anyOf(..)</li>
<ul>
<li>Match if the arg value matches ONE OR MORE internal matchers.</li>
</ul>
<li>fflib_Match.noneOf(..)</li>
<ul>
<li>Match if the arg value matches NO internal matchers.</li>
</ul>
<li>fflib_Match.isNot(..)</li>
<ul>
<li>Special case of fflib_Match.noneOf, with one internal matcher. Match if the arg value DOESN’T match the internal matcher.</li>
</ul>
</ul>
<br />
<br />
<pre class="prettyprint lang-java linenums">fflib_ApexMocks mocks = new fflib_ApexMocks();
fflib_MyList.IList mockList = new fflib_Mocks.Mockfflib_MyList(mocks);
mocks.startStubbing();
mocks.when(mockList.get(1)).thenReturn('Concrete');
mocks.when(mockList.get((Integer)fflib_Match.anyOf(
fflib_Match.isNull(), fflib_Match.integerMoreThan(3)
))).thenReturn('Matcher');
mocks.stopStubbing();
System.assertEquals('Matcher', mockList.get(null));
System.assertEquals('Matcher', mockList.get(84579));
System.assertEquals('Concrete', mockList.get(1));</pre>
<br />
<h1>
<span style="font-weight: normal;">Overloading Matchers</span></h1>
As with old school mocks, you can stub/verify the same method with multiple sets of argument values.<br />
<br />
So if I have a method called Integer doStuff(Integer x), and I want to stub different values if it is called with 0, 1 or 2, I simply declare multiple when/thenReturns (see code sample below).<br />
<br />
I can do exactly the same thing with matchers.<br />
<br />
When you overload a method with matchers or concrete argument return values, actual argument values are compared to the expected argument values in REVERSE ORDER. This is consistent with behaviour in Mockito. This is the case even if you use concrete expected values, then matchers, then concrete, then matchers again.<br />
<br />
<pre class="prettyprint lang-java linenums">fflib_ApexMocks mocks = new fflib_ApexMocks();
fflib_MyList.IList mockList = new fflib_Mocks.Mockfflib_MyList(mocks);
mocks.startStubbing();
mocks.when(mockList.get(1)).thenReturn('One'); //Concrete
mocks.when(mockList.get(fflib_Match.integerBetween(0, 10))).thenReturn('0..10'); //Matchers
mocks.when(mockList.get(3)).thenReturn('Three'); //Concrete again
mocks.stopStubbing();
System.assertEquals('Three', mockList.get(3)); //NOT 0..10!
System.assertEquals('0..10', mockList.get(2));
System.assertEquals('0..10', mockList.get(1)); //NOT One!</pre>
<br />
<h1>
<span style="font-weight: normal;">
Implementing custom matchers</span></h1>
<h4>
1. Considerations</h4>
Do you really need a new custom matcher?<br />
Can you reuse one of the ApexMocks standard matchers, or other matchers defined in your product already?<br />
Can you combine matchers to achieve the matching behaviour you want?<br />
Do you really need a new custom matcher definition?<br />
Can you get the same result by adding a new helper to register an existing matcher definition?<br />
For example, in Apex an Integer is a kind of Decimal. A Long is a kind of Decimal. So in fflib_MatcherDefinitions, there are Decimal matchers. In fflib_Match, there are matcher registration helpers for integer, long and decimal, but they all use the Decimal matcher definitions.<br />
<br />
<h4>
2. Implement fflib_IMatcher</h4>
To create custom matchers, define a class that implements fflib_IMatcher.<br />
You will need to implement the Boolean matches(Object arg) method defined on that interface.<br />
<br />
Simple example:<br />
<br />
<pre class="prettyprint lang-java linenums">//You can make this inner class private if it's only used in this test class
private class IntegerIsOdd implements fflib_IMatcher
{
public Boolean matches(Object arg)
{
//Custom matching logic.
//Make sure you think about null args, or args of the wrong type.
return (arg != null && arg instanceof Integer)
? Math.mod((Integer)arg, 2) != 0 : false;
}
}</pre>
<br />
More complicated example:<br />
This matcher requires supplementary information to determine if an argument is a match, which is specified in the constructor and stored as an internal variable.<br />
<br />
<pre class="prettyprint lang-java linenums">private class IntegerIsMultipleOf implements fflib_IMatcher
{
//Consider making internal variables final
private final Integer toMatch;
IntegerIsMultipleOf(Integer toMatch)
{
//Make sure you validate supplied args
if (toMatch == null)
{
throw new fflib_ApexMocks.MockException('Arg cannot be null: toMatch');
}
this.toMatch = toMatch;
}
public Boolean matches(Object arg)
{
return (arg != null && arg instanceof Integer)
? Math.mod((Integer)arg, toMatch) == 0 : false;
}
}</pre>
<br />
<h4>
3. Add helper to register the matcher</h4>
You must call fflib_Match.matches(fflib_IMatcher matcher). This returns an Object whose value is null.<br />
As Apex has no primitives, everything is a kind of Object. This means you can safely cast the returned value to the right argument type for your method argument.<br />
<br />
<br />
For example, if I want to stub fflib_MyList.IList.get(Integer myIndex), I need to ensure I call the method with an Integer argument.<br />
I can create helper methods to construct and register the appropriate matcher type and return an Object of the correct type for the method argument, which means the cast isn’t required in the actual test. This makes the test more readable (see code sample below).<br />
<br />
<pre class="prettyprint lang-java linenums">private static Integer integerIsOdd()
{
return (Integer)fflib_Match.matches(new IntegerIsOdd());
}
private static String integerIsMultipleOf(Integer toMatch)
{
return (Integer)fflib_Match.matches(new IntegerIsMultipleOf(toMatch));
}
</pre><br />
<pre class="prettyprint lang-java linenums">
// Given
fflib_ApexMocks mocks = new fflib_ApexMocks();
fflib_MyList.IList mockList = new fflib_Mocks.Mockfflib_MyList(mocks);
// When
mockList.get(1);
mockList.get(2);
mockList.get(3);
mockList.get(4);
mockList.get(5);
// Then
((fflib_MyList.IList) mocks.verify(mockList, 3)).get(isOdd());
//Is easier to read but entirely equivalent to...
((fflib_MyList.IList) mocks.verify(mockList, 3)).get((Integer)fflib_Match.matches(new isOdd()));
</pre>
<br />
<h1>
<span style="font-weight: normal;">
Limitations</span></h1>
<h1>
<span style="font-weight: normal;">Mixing matcher and non-matcher args</span></h1>
You cannot call a method with a mix of matcher and non-matcher arguments. The code will compile, but will throw an exception at runtime. The exception message will tell you that you cannot mix matchers and non-matchers in a single method call.<br />
<br />
This behaviour matches Mockito.<br />
<pre class="prettyprint lang-java linenums">//All matchers - fine
mocks.when(mockList.get2(fflib_Match.anyInteger(), fflib_Match.anyString())).thenReturn('matcher');
//All concrete - fine
mocks.when(mockList.get2(1, 'hello')).thenReturn('concrete');
//Mix of matchers and non-matchers - not fine
mocks.when(mockList.get2(1, fflib_Match.anyString())).thenReturn('mix');
//All matchers again - fine
mocks.when(mockList.get2(fflib_Match.eqInteger(1), fflib_Match.anyString())).thenReturn('mix');</pre>
<br />
Registering matchers up-front, rather than as the method is being called.<br />
<br />
You can declare matcher definitions up front, but you must register them during the method calls.<br />
<br />
Matchers rely on the registration occurring immediately before the method to be stubbed/verified is called. After the method is called in stub/verify mode, it clears the matchers so the next method has its own set.<br />
<br />
So this code will fail:<br />
<pre class="prettyprint lang-java linenums">// Given
fflib_ApexMocks mocks = new fflib_ApexMocks();
fflib_MyList.IList mockList = new fflib_Mocks.Mockfflib_MyList(mocks);
//Registers the matcher and returns null, but the framework doesn't know
//which method the call is associated with
Integer intBetween = fflib_Match.integerBetween(1, 10);
// When
mockList.get(2);
// Then
//Not using the matcher, instead supplying a concrete argument whose value is null
((fflib_MyList.IList) mocks.verify(mockList)).get(intBetween);</pre>
<br />
But this code will pass:<br />
<pre class="prettyprint lang-java linenums">//Declare the matcher definition but DON'T register it.
fflib_IMatcher hello1To3 = new fflib_MatcherDefinitions.Combined(fflib_MatcherDefinitions.Connective.AT_LEAST_ONE, new fflib_IMatcher[]{
new fflib_MatcherDefinitions.StringContains('Hello1'),
new fflib_MatcherDefinitions.StringContains('Hello2'),
new fflib_MatcherDefinitions.StringContains('Hello3')
});
fflib_ApexMocks mocks = new fflib_ApexMocks();
fflib_MyList.IList mockList = new fflib_Mocks.Mockfflib_MyList(mocks);
mocks.startStubbing();
mocks.when(
mockList.get2(fflib_Match.anyInteger(),
(String)fflib_Match.matches(hello1To3)))
.thenReturn('any');
mocks.when(
mockList.get2(fflib_Match.integerLessThan(5),
(String)fflib_Match.matches(hello1To3)))
.thenReturn('<5');
mocks.stopStubbing();
System.assertEquals('any', mockList.get2(8, 'Hello1'));
System.assertEquals('any', mockList.get2(8, 'Hello2'));
System.assertEquals('<5', mockList.get2(3, 'Hello1'));
System.assertEquals('<5', mockList.get2(3, 'Hell
</pre>
You may find this useful if you are setting up complicated matcher definitions, and want to reuse them. In this example, you could create a constant for the hello1To3 matcher definition.
<br />
<br />
<h1>
<span style="font-weight: normal;">
Reference</span></h1>
<h1>
<span style="font-weight: normal;">
Standard matchers in ApexMocks</span></h1>
<br />
<table><tbody>
<tr><td><b>Matcher</b></td><td><b>fflib_Match helpers</b></td></tr>
<tr><td><b>Equals</b></td><td>eq, refEq, (eq… all primitive types)</td></tr>
<tr><td><b>Any</b></td><td>any, anyBLA, (any… all primitive types)</td></tr>
<tr><td><b>Date</b></td><td>dateAfter, dateBefore, dateBetween</td></tr>
<tr><td><b>Datetime</b></td><td>dateTimeAfter, dateTimeBefore, dateTimeBetween</td></tr>
<tr><td><b>Decimal</b></td><td>decimalBetween, decimalLessThan, decimalMoreThan</td></tr>
<tr><td><b>Double</b></td><td>doubleBetween, doubleLessThan, doubleMoreThan</td></tr>
<tr><td><b>FieldSet</b></td><td>fieldSetEquivalentTo </td></tr>
<tr><td><b>Integer</b></td><td>integerBetween, integerLessThan, integerMoreThan</td></tr>
<tr><td><b>Nullchecks</b></td><td>isNotNull, isNull</td></tr>
<tr><td><b>List</b></td><td>listContains, listIsEmpty, listIsNullOrEmpty</td></tr>
<tr><td><b>Long</b></td><td>longBetween, longLessThan, longMoreThan</td></tr>
<tr><td><b>SObject</b></td><td>sObjectOfType, sObjectWith, sObjectWithId, sObjectWithName</td></tr>
<tr><td><b>String</b></td><td>stringContains, stringEndsWith, stringIsBlank, stringIsNotBlank, stringMatches, stringStartsWith</td></tr>
</tbody></table>
<div>
<br />
Note, some of these methods are overloaded rather than proliferating helper methods.<br />
<pre class="prettyprint lang-java linenums">
//Instead of having...
integerBetweenInclusiveLowerInclusiveUpper(Integer lower, Integer upper)
integerBetweenInclusiveLowerExclusiveUpper(Integer lower, Integer upper)
integerBetweenExclusiveLowerInclusiveUpper(Integer lower, Integer upper)
integerBetweenExclusiveLowerExclusiveUpper(Integer lower, Integer upper)
//We actually have:
integerBetween(Integer lower, Integer upper)
//Which calls into this method, passing in false for inclusiveUpper and inclusiveLower
integerBetween(Integer lower, Boolean inclusiveLower, Integer upper, Boolean inclusiveUpper)</pre>
<br />
<h1>
<span style="font-weight: normal;">
More code examples</span></h1>
Search for the fflib_Match in <a href="https://github.com/financialforcedev/fflib-apex-mocks/blob/master/src/classes/fflib_ApexMocksTest.cls" target="_blank">fflib_ApexMocksTest</a>.<br />
<br />
</div>Anonymoushttp://www.blogger.com/profile/05661077803935743665noreply@blogger.com2