Verify call’s order in Apex unit tests

Have you ever though to verify the order of a method call in your Apex unit tests?

With Apex Mocks you can.

Sometimes ensure that a method is called after another one can add that plus strength to your unit tests.

Let’s have a look at an example: In our example, I’ll use a hypothetical API service that follows the Enterprise Pattern from the fflib-apex-common. This API performs few reads of data, it process that data and then consolidate it using the fflib_ISObjectUnitOfWork.

Does it sound complicated? Let’s have a look at the code

Screen Shot 2017-03-26 at 12.31.46

This it’s calling three selectors ( Opportunity, OpportunityLineItem and Contact) to read some data, then it calls the doStuff() method to perform some operation on these data, and then it registers some data in the UnitOfWork to be stored permanently.

Let’s say that the order on which the selectors are called matters for the correct reading of the data and we want to test it. (the MY_MOCKS variable is the Fflib_ApexMocks instance).

Screen Shot 2017-03-26 at 12.47.48

In the first part, we mock out the methods that interact with Our API. The magic is in the second part of the test method.

Screen Shot 2017-03-26 at 17.25.31

First of all, we create an instance of the fflib_inOrder to tell to Apex Mocks on which mock instance we want to verify in order. Those are passed as a list of objects together with the fflib_ApexMocks instance, however, the order they appear in that list is not relevant to the final verification.

Then is the time to call the method to test, followed by the three verify to assert the correct order of call.

There are three main differences with a classic Apex Mocks verification, firstly the verify method is called on the fflib_inOrder instance instead of the fflib_ApexMocks instance, then we have to specify explicitly the counter through the calls(Integer times). This is due to the fact that the counters ( times, atLeast, atMost ) are not currently fully implemented for the fflib_InOrder framework.

The last difference with a classic verify is that the order in which the verify are performed make the difference between a passing test and a one that fails. In our example, if we swap line 38 and 39 the test would fail.

Here is another example to show how the inOrder verify behaves;

Screen Shot 2017-03-26 at 17.39.57

this is going to test this part of our API method

Screen Shot 2017-03-26 at 17.41.41

In this test, we can observe that the inOrder with the calls(Integer times) performs a not greedy verification ( because it’s actually called three times ) and the fact that the inOrder is not strict ( in a strict verification the registerDirty is called once and then is called the registerNew ).

To perform a more strict verification we should verify every single call:

Screen Shot 2017-03-26 at 17.51.24

however, this test is not a fully strict verify, due to the not greedy nature of the verification. In fact, if in our example we call the registerNew at line 32 and 33 more times, this test still pass, but it won’t if we reduce them to only one.

With the fflib_InOrder you can finally check not only that your code is doing what you’d expect but that is done in the right order. This gives you the opportunity to write those tests that go forward the code coverage and makes your tests stronger and more descriptive. Happy unit testing.

Happy Mocking!

The code of those examples can be downloaded from my GitHub repo.

Other Resources:

Advertisements

GDPR and Apex Code, should you care?

In short: YES, a lot.

That’s it, post done.
Thank you for reading.

Hold on, not so fast…

Since the GDPR regulation became effective, there has been madness around it. It changed the way the private and sensible data should be handled by, introducing numbers of laws around it.
The GDPR is a European regulation, however, Salesforce is a worldwide environment, and that means that your software could be affected and needs to comply with the GDPR regulations.

Not convinced yet why should you care? Ok, let get more technical.

The declarative features of Salesforce enforce by default CRUD and FLS permissions. That means that a fellow #AwesomeAdmin can lock it down all the personal or sensitive data that a user shouldn’t see with few clicks.

However. Apex code runs typically in System mode, that means that CRUD and FLS are not enforced automatically.
You have to enforce them with some extra code.

Let’s see a real-life example:

You have been hired from Houdi-me. Houde-me sales mind cracking puzzles. Some of those puzzles are so hard to solve that they offer a phone help desk to give some hints to the customers when they are stuck and cannot get to the solution.
The Call centre operator needs to know the name of the customer and what puzzle the customer is struggling with but doesn’t need to see the email address or the phone number of the customer. You can remove access from the profile of the CallCentre operator easily. No code required.

However, the email is used from Houdi-me to promote new puzzles to the customers, but not all the customers want that promotional email. To simplify the process, Houdi-me decided to provide a custom button to the Call centre operator to “forget” some data od the customer, even if the user does not have access to that data.

It’s your time to shine. You have to write an extension of the Contact controller to “clean” some data from the record.

public PageReference forget(){
        
        customer.Email = '';
        update customer;
        
        PageReference pageRef = new PageReference('/'+customer.id);
        pageRef.setRedirect(true);
       	return pageRef;
    }

And voila’, you have just used the power of Apex to get around the FLS limitation for the Call centre User and cleaned the email without shown the data to the user; GDPR enforced.

At Houdi-me they encourage their customers to spread the love for their puzzles. A Customer can nominate up to three additional email addresses to give access to the help desk to friends or family members to which they have borrowed the puzzle. To manage those relationship-email pairs, Houdi-me will use a custom page and will give access only to selected users of the Call centre, because those are Personal Identifiable Informations.

Even if the fields have been looked down for the Call centre user, you cannot assume that the field would be protected from indiscreet eyes.

In the Visualforce page, the fields need to be displayed using the object fields using the standard object notation.

<apex:OutputText value="{!customer.FirstFriendEmail__c}" />

Using a similar notation the FLS is enforced, and the data will be concealed to who won’t have the right to see it.

Be careful about using custom getters to display the data like this.

<apex:OutputText value="{!FirstFriendEmail}" />

populated with Apex code like this

public String getFirstFriendEmail(){
    return customer.FirstFriendEmail__c; 
}

Because in this case, you are introducing a CRUD & FLS Violation and if a standard Call centre user would gain access to the page it will see data that is not supposed to see. GDPR problem. In fact, the custom getter is treated as a simple string, not as data from the object.

The CRUD & FLS violation should be prevented on data update as well.
For this reason, the DescribeSObjectResult class includes some helper functions to check the user’s accessibility to the data.

  • IsCreateable()
  • IsAccessible()
  • IsUpdateable()
  • IsDeleteable()

In our case

//Let’s assume we have fetched contact “c” from a SOQL query
if (!Schema.sObjectType.Contact.fields.FirstFriendEmail__c.isUpdateable()){
    ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR,'Error: Insufficient Access'));
    return null;
}
c.FirstFriendEmail__c=firstFriendEmailValue;
update c;

would be used to update the customer record only when the user has access to the field, and degrade gracefully in case the access to the field is not granted.

But another question is still open:
Is the user allowed to access the record?

Houdi-me has two main call centres, and they have their pool of customers to look after. One call centre user shouldn’t be able to see the data of a customer served from the other call centre. The Sharing architecture of Salesforce allows to achieve this quite quickly, but Apex doesn’t enforce the sharing rules unless explicitly said so.
The with sharing keyword used in the class declaration enforces the Sharing in the methods of the class.

Apex leave you the option to chose whether or not enforce the sharing rules in your code:

public with sharing MyClassWithSharing{}
public without sharing MyClassWithout{}
public MyClass{}

the first class will enforce the sharing the second Class will skip the sharing and the third could depend from the caller or whether the class implements an interface or extends another class and what type of sharing the caller or the parent class enforces.

To a more exhaustive explanation of the sharing have a look at the SF documentation.

As you can see Apex a Visualforce leave to you the freedom and the power to control who should access the data, and is this flexibility that let you implement the right level of access to comply with the GDPR regulation.

 

Resources:

GDPR regulation

Trailhead Data Leak Prevention Module

 

I’ve attended SurfForce 2018 and was awesome, here’s why.

 

This has been the second Salesforce event that I’ve attended, and I’m getting to the conclusion that the community events like SurfForce are a fantastic opportunity that everyone should take.
My journey to SurfForce started when I saw the Twitter post from @SurfForce giving the dates and the location. I decided to go. I then sorted out the logistic: the holidays from work, B&B and of course the ticket for the event. It was in April.

Thursday 12th of July

time to go… long trip driving from Harrogate to Newquay with the car. Dedication? Craziness? You chose…
Arrived in Newquay in the evening I went around to have a taste of the beauty around Newquay.

meInNewquay.jpeg

Friday 13th of July

surfing time… 😀

Hold on… that is the same beach of yesterday… the rock with the house on top… where the water went? I never saw a tide like that…

WhatsApp Image 2018-07-14 at 07.58.19.jpeg
The surfing was super fun, but first …. let’s help Surfers Against Sewage to keep the beach clean.

WhatsApp Image 2018-07-14 at 07.57.18.jpeg
During the dinner three fantastic inspirational talks from
SOPHIE HELLYER @sophiehellyer
NATALIE FEE @nataliefee
HUGO TAGHOLM @HugoSAS

WhatsApp Image 2018-07-19 at 14.01.50.jpeg
They brought their passion and love for the ocean and the environment and their dedication on how they fight day by day against plastic pollutions.

Saturday 14th

Talks time:

Laura Walker told us how you can make your data ready for the AI

Paul Harris gave us some tips on how to have a successful project delivered

Michaela Grill the Salesforce Employee Resource Group “Earthforce” leader in London told us how to inspire EMPLOYEES TO DRIVE POSITIVE ENVIRONMENTAL IMPACT

Barry Roberts from Bluewolf explained How to reduce costs and help the environment with Field Service Lightning

Alberto Fernàndez and Francisco Porras from FinancialForce gave us a fascinating overview of the Developer console and how to use it in Admin tasks

Rob Fay demonstrate how the Platform Events lead to a new architecture of Salesforce solutions

Carl Dempsey the Vice President, Solution Engineering, EMEA at Salesforce showcased the story of an early adopter of Einstein Vision

Tzhe’ela Trooper described the use of Einstein for the NonProfit Organizations

Patrick Schwarz from Sapient I7 demonstrate how to take care of the candidate engagement using Salesforce

Will Coleman, the Director of Experiential Marketing EMEA at Salesforce.org, talked about his commitment to helping the ocean

Finally a fantastic presentation from Carl Dempsey and Charlie Isaacs the VP and CTO for Customer Connection at SalesForce.
A Genious orchestration of Heroku, Platform Events, Einstein Vision and FLS.
Spot littered beaches using pictures from drones, analyse them with Einstein Vision and coordinate the volunteers with FLS.

SurfForce Plastic pledge

The organizers asked us to do a little plastic pledge. Giving up on the use of some plastic to improve our impact on Mother Hearth. They gave us some suggestions, and what I found that in a way I was already doing my bit without realising.

WhatsApp Image 2018-07-20 at 08.50.07

I normally use a RocketBook to take my notes and sketches. It’s a magic notepad, you write on it with a pen that is erasable by friction, you can clean the whole page with a wet wipe. You need to save your work, no problem: take a picture with the App and that will send automatically to an email address, slack, Evernote, Dropbox, OneDrive and many others. No more trees will die to host my awful handwriting

I love Salesforce’s water bottles, I just use all the time, at the gym, going out on the motorbike.

I switched to soap bars. That reduces the number of plastic bottles, and I’m a #BaldForce member so no shampoos for me 😀 .

 

What have I learned from SurfForce?

  • There is a lot to do for our environment and everyone should keep in mind that :
  • We Do Not Inherit the Earth from Our Ancestors; We Borrow It from Our Children
  • Little changes in our lifestyle can have greats benefit
  • without knowing I was already doing my bit, but I can do better.
  • I’m a terrible photographer

For me, events like SurfForce are a fantastic opportunity to make new friends, talk about ideas for a future meetup find old friends.
SurfForce is a bit more, the weather, the surfing…
SurfForce it was a kind of intimate event, and you can have the time and the opportunity to speak with almost everyone, remember and be remembered by everyone.

To Shaun, Kerry, Scott, Marcelle and Mick many many thanks for organising that and see you all next year.

 

Screen Shot 2018-07-20 at 08.34.39.png

Counters in the InOrder world: times()

If you are reading this post, probably you had already a glance to my previous post about the Counters. You have probably noticed that the Counters functionality was somehow incomplete. Some of the counter where available for the classic verification but not for the ordered verification.

But not anymore.

Apex Mocks it’s getting more flexible and powerful allowing you to specify strict and loose counters in the inOrder verification.

Let’s go through them one by one and discover their power.

times()

((ISoftwareDeveloper) inOrder.verify(
    mockProductiveDeveloper, MY_MOCKS.times(15))).drinkCoffe();

You can immediately understand that our software is having quite a lot of coffees during the day. But there are some details that need to be mentioned. Let’s have a look at this snippet of pseudo code.

1 softDev.writeSomeCode();
2 softDev.writeSomeCode();
3 softDev.drinkCoffe();

4 softDev.writeSomeCode();
5 softDev.blameSlowNetwork();

6 softDev.writeSomeCode();
6 softDev.writeSomeCode();
7 softDev.gitCommit();

We all are productive developers, so let’s concentrate on the writing of the code, and verify how many times the code has been written.

The times allows us to check how many times the method has been called, and this verification is not strict. What does it mean in code? If we write those verify as separate tests

((ISoftwareDeveloper) inOrder.verify(
    mockProductiveDeveloper, MY_MOCKS.times(2))).writeSomeCode();

((ISoftwareDeveloper) inOrder.verify(
    mockProductiveDeveloper, MY_MOCKS.times(1))).drinkCoffe();

and

((ISoftwareDeveloper) inOrder.verify(
    mockProductiveDeveloper, MY_MOCKS.times(3))).writeSomeCode();

((ISoftwareDeveloper) inOrder.verify(
    mockProductiveDeveloper, MY_MOCKS.times(1))).blameSlowNetwork();

they would both pass.

Yep, you read correctly, they both pass. Why? It’s related to the instances consumed in the verification. For each test, the inOrder goes through all the instances of the mock that we are verifying and sequentially consumes the available invocations.

The first tests first check that writeSomeCode() method is called twice, then looks for the drinkCoffe() and it’s happy. Quite straight forward.

The second Test instead is a bit more tricky. This needs three instances of the writeSomeCode(). Starting from the beginning it has two instances, then have the drinckCoffe() in the middle that is ignored and then it finds the third. After that, the instance available to be verified is blameSlowNetwork().

I’m sure you can tell me how many available instances there are for the writeSomeCode() before our dev would final commit the work on git.

On the other side

((ISoftwareDeveloper) inOrder.verify(
    mockProductiveDeveloper, MY_MOCKS.times(4))).writeSomeCode();

((ISoftwareDeveloper) inOrder.verify(
    mockProductiveDeveloper, MY_MOCKS.times(1))).gitCommit();

and

((ISoftwareDeveloper) inOrder.verify(
mockProductiveDeveloper, MY_MOCKS.times(3))).writeSomeCode();
((ISoftwareDeveloper) inOrder.verify(
mockProductiveDeveloper, MY_MOCKS.times(1))).blameSlowNetwork();
((ISoftwareDeveloper) inOrder.verify(
mockProductiveDeveloper, MY_MOCKS.times(1))).writeSomeCode();

both would fail.

One last detail, the default verification for the InOrder is now “times(1)”. that means that I could happily not specify in my examples whether instead, I kept for readability.

As you can see the power and the flexibility of the InOrder verification have grown with this new counter adding a functionality that was some how missing.

Counters in the InOrder world: atLeast() vs. calls()

This is another chapter of our little story through the ApexMocks counters for the inOrder verification.

You have probably already encountered the first chapter about the inOrder verification.

You have probably noticed that those verifications were some how incomplete in their functionality, but not anymore.

The times() added the possibility to verify the exact number of calls, and then the atLeast() get along. The atLeast() and calls() both set the minimum number of calls that are expected for that method, but they behave differently. The atLeast() is greedy, instead the calls() is not.

Greedy? Not greedy? What does it mean?

To explain the greedy concept we first need to talk about the invocation consumed.

The inOrder stores the invocations methods called on the mocked instance sequentially, in a list. Then when we verify the inOrder goes through this list and “consume” those invocations.

The atLeast(), being greedy, would consume all the invocations that match the method under verification instead, the calls() would consume only those that have been verified.

Let’s see with some pseudo code.

1 softDev.writeSomeCode(); 
2 softDev.writeSomeCode(); 
3 softDev.drinkCoffe(); 
4 softDev.writeSomeCode(); 
5 softDev.blameSlowNetwork(); 
6 softDev.writeSomeCode(); 
7 softDev.writeSomeCode(); 
8 softDev.gitCommit();

performing the following verify

((ISoftwareDeveloper) inOrder.verify( 
     mockProductiveDeveloper, MY_MOCKS.calls(2))).writeSomeCode();

the next available invocation to be verified in this case would be the drinkCoffe() at line 3.

Using the atLeast() instead

((ISoftwareDeveloper) inOrder.verify( 
    mockProductiveDeveloper, MY_MOCKS.atLeast(2))).writeSomeCode();

since it consumes all the invocations that match our method, the next method that is available to be verified is gitCommit() at line 8.

Apex Mocks behaves as close possible to Mockito and for the greedy verification, this page of the Mockito documentation would explain how this concept works.

A special version of the atLeast() is also available: atLeastOnce(). Guess how many time is expected to be called…

The adding of atLeast() counter for the inOrder verification it narrows down the gap of funcionality between ApexMocks and Mockito, giving more flexibility to your mock verification.

Happy mocking.

Stub multiple calls

One of the basics concepts in Mocking is the stubbing.

Wikipedia gives us this definition of stubbing:

“A method stub or simply stub in software development is a piece of code used to stand in for some other programming functionality. “

In ApexMocks we can stub a method to return a value, or to throw an exception. We can even specify a custom answer when we need more flexibility and power.

Historically the stubbing with ApexMocks can handle only one value at the time.

What if you need to stub a method to return different values or different exceptions each time is called. Well, you could write an Answer and shape what you need, or…

Mockito, from which ApexMock get inspiration, have a specific syntax for that so why ApexMock shouldn’t have as well?

Now you can.

Every one of us has a friend that is not reliable time wise. They great, but they almost cannot make in time at a meeting. Everyone turned up, but not Franck. Then if you call them asking for an estimate of the time they would be there you get each time different time values… and potentially different excuses but for now, let’s keep it simple and concentrate on the times.

MY_MOCKS.when( everyoneTurnedUp.callFrankToFiguredOutWhenHisGonnaArrive()).
    thenReturn(new Time(15 minutes)).
    thenReturn(new Time(20 minutes)).
    thenReturn(new Time(10 minutes)).
    thenThrow(newBeInTimeException('we are all here, hurry up!')). //you are fed up of waiting and shout at Franck to be quicker
    thenReturn(new Time(0 minute)); //he finally arrived

When then you call the callFrankToFiguredOutWhenHisGonnaArrive() this would return 15 the first time, 20 the second, 10 the third, then would throw an exception for the fourth invocation, and for all the other subsequences calls would return zero.

This is possible because those methods can be chained together and define precisely what you need.

To be fair you could write that stubbing in a more compact way using the thenReturnMulti() stubbing method:

MY_MOCKS.when( everyoneTurnedUp.callFrankToFiguredOutWhenHisGonnaArrive()).
    thenReturnMulti(new List<Time>{minutes15, minutes20, minutes10}).
    thenThrow(new BeInTimeException('we are all here, hurry up!')).
    thenReturn(new Time(0 minute));

Similarly, there is the possibility to specify a sequence of Exception that you want to throw sequentially, with the thenThrowMulti() and a list of exceptions as the parameter.

thenReturnMulti() and thenThrowMulti() are not perfectly compliant with Mockito. Mockito uses the same name for the single argument version and the multiple argument version, using an iterable as the parameter of the multi. In Apex we are more limited. The thenReturnMulti() needs to take as a parameter  a list of objects,  and the thenReturn() needs an object. A list of objects is still an object, isn’t it?

If we stubbed N times a method and then the method is called more times than N, after N the value returned would be the one in the last stub. In our case after the fifth time, it would return constantly zero.

One last detail, if you stub the same method multiple times the last stub override the others, for example:

MY_MOCKS.when( everyoneTurnedUp.callFrankToFiguredOutWhenHisGonnaArrive()).
    thenReturn(new Time(5 minutes));

MY_MOCKS.when( everyoneTurnedUp.callFrankToFiguredOutWhenHisGonnaArrive()).
    thenReturn(new Time(10 minutes));

MY_MOCKS.when( everyoneTurnedUp.callFrankToFiguredOutWhenHisGonnaArrive()).
    thenReturn(new Time(11 minutes));

the last stub would override the two previous of 5 and 10 minutes.

The stubbing for sequential calls gives you the flexibility to cover those cases that a simple return is not enough, but with out need to write a custom answer.

Happy Mocking.

 

 

Counters in Apex Mocks verifications

The very core of the Mocks verification checks whether a method has been called or not. Typically with Apex Mocks you can achieve that  with

Screen Shot 2017-04-01 at 11.34.30

Screen Shot 2017-04-01 at 11.35.49

Since the beginning, Apex Mocks aimed to have a syntax that looks like Mockito. Recently Apex Mocks have narrowed the gap with Mockito, getting new counters functionality with a syntax that it’s getting closer to the well know Java mocking library.

Those new functionalities are:

  • times(Integer times)
  • description(String customAssertMessage)
  • atLeast(Integer atLeastTimes)
  • atMost(Integer atMostTimes)
  • atLeastOnce()
  • between(Integer atLeastTimes, Integer atMostTimes)
  • never()

Let’s have a look at some examples on how they work.

The following examples are contained in this ApexMocksExamples project, that you can download and do your Apex Mock experiments. This project is based on the fflib-apex-common and  Enterprise Patterns.

This is the method that we are going to test:

Screen Shot 2017-04-01 at 11.55.58.png

That is not particularly pretty or efficient but helps me to show how to test the calls on the registerDirty() method.

First of all, we need to stub OpportunitiesSelector Screen Shot 2017-04-01 at 11.59.52where mockOpportunities isScreen Shot 2017-04-01 at 12.02.41 then after have defined the mock for the class to test ( Fflib_ISObjectUnitOfWork ) and called the method to test

Screen Shot 2017-04-01 at 12.04.17.png

we can crack on with some verification:

We can verify that the method is called with a specific parameter:

Screen Shot 2017-04-01 at 17.02.52or better, since times(1) is the default behavior with

Screen Shot 2017-04-01 at 17.02.38

It’s easy to guess the next verification;

Screen Shot 2017-04-01 at 17.12.51

In case you want to assert that a method has NOT been called you can use times(0), or more elegantly

Screen Shot 2017-04-01 at 17.11.54

The times() and never method perform a strict validation on the number of times of the method’s calls, but sometimes you need to enforce looser controls and in that case:

atLeast()

Screen Shot 2017-04-05 at 18.26.56

atLeastOnce()

Screen Shot 2017-04-05 at 18.27.42

atMost()

Screen Shot 2017-04-05 at 18.27.03

between()

Screen Shot 2017-04-05 at 18.27.12

would give you more flexibility on your verifications.

The last feature is not a counter, but would help to give more readability and would help to debug a failing test, the description() method

Screen Shot 2017-04-01 at 17.27.08

would add a custom message in case the test fails.

All those methods can be combined together:Screen Shot 2017-04-01 at 17.32.56

With those new features of Apex Mocks you can make your tests even more beautiful.

Happy Mock Testing.

 

The code of those examples can be downloaded from my GitHub repo.

 

Other Resources:

Answering with Apex Mocks

Sometimes stub a method to return a canned value is not enough.

Sometimes the method you are mocking has some side effect.

Sometimes you need to stub your method with a custom answer.

With Apex Mocks you can bring your stubbing to another level and cover those more complex scenarios, let’s see how.

Let’s say that you are testing n API that calls a method that has a side effect, like this one:

Screen Shot 2017-03-28 at 21.42.23

In our example, the method takes two lists of SObjects and copies the elements in those lists, actually doubling the size of the lists, but it returns only the contacts list.

In our test, to take control of what the doStuff() method return we can stub with a “classic” thenReturn(), or if we need a more complex stubbing use the following:

Screen Shot 2017-03-28 at 22.04.11

what is thenAnswer(new MyAnswer()) doing?

Setting the stubbed value to use a custom Answer.

Unlikely Mockito in Apex we cannot define the answer directly in the stubbing, but we have to define separately the answer and pass an instance to the method.

We can define and shape the answer as we need by implementing the fflib_Answer interface and the related answer() method:

Screen Shot 2017-03-30 at 21.07.02.png

ApexMock would then invoke the callback method “answer()” performing the operations that you have defined.

The fflib_InvocationOnMock instance in the parameter of  the answer method gives us access to some handy data about the invocation that we are stubbing, in particular:

Screen Shot 2017-03-30 at 21.13.25

we can then use the arguments of the invocation of the stubbed method to define our Answer, for example:

Screen Shot 2017-03-30 at 21.25.52

We decided to take the List of OpportunityLineItem and copy for three times, instead of two of the real implementation.

but, first let’s have a look at the method we want to test:

Screen Shot 2017-03-30 at 21.43.56

Let’s concentrate for a second on the List<OpportunityLineItem> oppLines returned by the selector, that is then passed to the service, and saved in the DB through the unit of work.

As we said previously the doStuff method have a side effect on the Opportunity lines, now we have to test that the registerDirty is called with the expected lines:

Screen Shot 2017-03-30 at 21.48.21.png

breaking down the method we have the stubbing

Screen Shot 2017-03-30 at 21.58.29

where mockLines is

Screen Shot 2017-03-31 at 21.49.29

and since the answer triplicates the lines the expected lines to be registered in the unit of work would be

Screen Shot 2017-03-31 at 21.54.56.png

The Answer is a new powerful tool in your Apex Mocks tool box that would help where the classic stubbing is not enough.

Happy mocking.

The code of those examples can be downloaded from my GitHub repo.

Other Resources: