0

Given

  • Some class MyCode that calls a void method in MyService.doStuff(SObjectType sobjType)
  • You plan on stubbing MyService.doStuff(...) to make unit testing of MyCode easier, faster, and more rigorous.
  • Your testmethod intends on calling doStuff(...) twice and the first time you call it, you want an exception to be thrown but the second time, you want no exception thrown

How do you do this in ApexMocks?

0

Assuming you have the latest ApexMocks github library (you'll need at least from July 20 2017)

Assuming you are using an Application class and standard fflib pattern for injecting mocks into the factory pattern

@IsTest
private static void myTest () {
    fflib_ApexMocks mocks = new fflib_ApexMocks();

    // Given mock Service class
    MyServiceImpl mockMyService = (MyServiceImpl) mocks.mock(MyServiceImpl.class);
    // Given stubbing of doStuff method
    mocks.startStubbing();
    ((IMyService) mocks.doThrowWhen(new list<Exception> {
                    new MyService.MyException(), // throw exception on first call
                    null},                       // no exception on second call
            mockMyService)).doStuff(Account.SObjectType);
    mocks.stopStubbing();
    // Given mock Service injected
    Application.Service.setMock(IMyService.class,mockMyService);

    // when service called a first time
    try {
        MyService.doStuff(Account.SObjectType);
        System.assert(false,'snb, we stubbed an exception to be thrown on 1st call');
    }
    catch (Exception e) {
      System.assert(e instanceof MyService.MyException);
    }

    // when service called a second time
    try {
        MyService.doStuff(Account.SObjectType);
    }
    catch (Exception e) {
        System.assert(false,'snb, we stubbed no exception to be thrown on 2nd call');
    }
}

How does this work?

  • mocks is the object that holds everything together in ApexMocks
  • MyService.doStuff(..) is the redirector to the factory pattern that returns an object MyServiceImpl that implements IMyService with method doStuff(..). This is all standard fflib Service layer setup to exploit mocking and is well-covered in Andrew Fawcett's blog posts 1 and 2 on Unit Testing.

  • MyServiceImpl mockMyService = (MyServiceImpl) mocks.mock(MyServiceImpl.class); constructs a mock service object that will be injected a bit later.

  • The stubbing code segment below is the key to it all:

    mocks.startStubbing();
        ((IMyService) mocks.doThrowWhen(new list<Exception> {
                        new MyService.MyException(), // throw exception on 1st call
                        null},                       // no exception on 2nd call
                mockMyService)).doStuff(Account.SObjectType);
    mocks.stopStubbing();
    
  • mocksDoThrowWhen takes two args: a singleton (or list) Exception and the mock object being stubbed (in our case, a mock MyService)

  • In ((IMyService) mocks.doThrowWhen(..)).doStuff(..); you are telling the stubbing layer that when doStuff is called with a matching argument, that on the first call, the exception is thrown but because the second element in the exception list is null, no exception is thrown on the second call)

But wait, there's more ..

Note that the you can do even more with this such as throwing FooException on the first call, no exception on the second call, and BarException on the third call using this construction:

mocks.startStubbing();
((IMyService) mocks.doThrowWhen(new list<Exception> {
                   new MyService.FooException(), // throw exception on 1st call
                   null,                         // no exception on 2nd call
                   new MyService.BarException() // throw exception on 3rd call
                   },                       
               mockMyService)).doStuff(Account.SObjectType);
mocks.stopStubbing(); 

This helps in testing your handling of multiple exception types in MyCode.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.