3

Bit of a repost, but a certain catch-22 about not having enough reputation means I can't comment on any of the duplicate threads! (cough cough)

I'm trying to test the catch block of a try-catch using Mockito; is it possible to make a mock throw an exception that is handled by the method being tested? I can't use doThrow()...when()... or @Test(expected = Foo.class) because the exception is handled. I want to test that the method handles Exceptions properly.

@Controller
public class StockExchangeController {

    public ModelAndView placeOrder(ModelAndView mav, MyObj myObj) {

        try {
            validator.validate(myObj); // Throws CustomException if validation fails
            mav.setViewName("successPage");

        } catch (CustomException ex) {
            mav.setViewName("failPage");
        }

        return mav;
    }
}

I'd like to be able to stub the behaviour of my "validatorObject", like

doThrow(new CustomException()).when(validatorMock).validate();

Is there a way to do this?

The answer here (Test catch block logic with Junit and mockito) doesn't work (I believe) because the exception is handled before it gets to test level.

Tips and thoughts much appreciated!

2
  • Where does the validator come from? Is it a member in the StockExchangeController ? If so, where is it intialized? Commented Jun 23, 2018 at 16:45
  • validator actually comes from an @Autowried ValidatorFactory class that gets the appropriate validator - basically new MyObjValidator() instantiated in the Factory. Commented Jun 24, 2018 at 12:23

2 Answers 2

1

BDD Style Solution

Mockito alone is not the best solution for handling exceptions, use Mockito with Catch-Exception

Mockito + Catch-Exception + AssertJ
given(otherServiceMock.bar()).willThrow(new MyException());

when(myService).foo();

then(caughtException()).isInstanceOf(MyException.class);
Sample code Dependencies
  • eu.codearte.catch-exception:catch-exception:1.3.3
  • org.assertj:assertj-core:1.7.0
Disadvantage
  • Only Mockito up to 1.10.8 is supported
Sign up to request clarification or add additional context in comments.

2 Comments

Too much boldface. OTOH I looked at your raw text and I'm disappointed that SO markdown doesn't handle minor heading better.
Easily fixed. Use # headings instead.
1

Why should doThrow(..).when(..)... not work?

The placeOrder method catches the exception and returns a testable result:

@RunWith(MockitoJUnitRunner.class)
public class TestStockExchangeController {

    @Mock
    Validator validator;

    @Mock MyObj myObj;

    @Test
    public void testException() throws CustomException {
        StockExchangeController sec = new StockExchangeController(validator);
        doThrow(new CustomException()).when(validator).validate(myObj);

        ModelAndView modelAndView = sec.placeOrder(new ModelAndView(), myObj);
        assertEquals("failPage", modelAndView.getViewName());
    }
}

I've tested with these two files:

Main source code:

//src/main/java/StockExchangeController.java
public class StockExchangeController {

    private ValidationFactory factory;

    public ModelAndView placeOrder(ModelAndView mav, MyObj myObj) {
        Validator validator = factory.getValidator("S");
        try {
            validator.validate(myObj); // Throws CustomException if validation fails
            mav.setViewName("successPage");
        } catch (CustomException ex) {
            mav.setViewName("failPage");
        }
        return mav;
    }
}

class CustomException extends Exception {}

interface ValidationFactory {
    Validator getValidator(String s);
}

class Validator {
    public void validate(MyObj myObj) throws CustomException {}
}

class ModelAndView {
    private String viewName;

    public void setViewName(String viewName) {
        this.viewName = viewName;
    }

    public String getViewName() {
        return viewName;
    }
}

class MyObj {}

Test source code:

//src/test/java/TestStockExchangeController.java
//various imports
@RunWith(MockitoJUnitRunner.class)
public class TestStockExchangeController {

    @Mock
    private Validator validator;

    @Mock
    private ValidationFactory validationFactory;

    @InjectMocks
    StockExchangeController target = new StockExchangeController();

    @Test
    public void testException() throws CustomException {
        MyObj myObj = new MyObj();
        when(validationFactory.getValidator(anyString())).thenReturn(validator);
        doThrow(new CustomException()).when(validator).validate(myObj);

        ModelAndView modelAndView = target.placeOrder(new ModelAndView(), myObj);

        assertEquals("failPage", modelAndView.getViewName());
        verify(validator).validate(myObj);
    }
}

2 Comments

I think it may be my poor understanding of Mock objects? My validator Mock is actually returned by a ValidationFactory which gets the appropriate Validator implentation: when(validationFactory.getValidator(any(TransactionForm.class))).thenReturn(ransactionValidatorMock); I then also add doThrow(new CustomException).when(transactionValidatorMock).validate();, but verify(transactionValidatorMock).validate(); gets zero interactions. Does my approach make sense?
@DannySalvadori The approach makes sense. Are you mocking the correct method? You write about mocking with doThrow(new CustomException).when(transactionValidatorMock).validate();, but the actual call is validator.validate(myObj);, which would require mocking with doThrow(new CustomException).when(transactionValidatorMock).validate(myObj);

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.