Mocking exceptions with Mockito: Unexpected Exception error - java

This is the code of my service:
public class Service {
public Object serviceMethod() throws MyException {
#Autowired
Dao dao;
try {
return dao.dao_method();
} catch (ExceptionFromDaoMethod e) {
throw (new MyException());
}
}
}
I want to write a simple Unit Test; this is my test case:
#Mock
Dao dao;
#InjectMocks
Service service;
#Test(expected=MyException.class)
public void shouldReturnMyException throws MyException {
when(service.serviceMethod()).thenThrow(new MyException());
}
This test fails, because I have an Unexpected exception:
the expected exception is MyException but was
org.mockito.exception.base.MockitoException
Why? What is the solution?
Update
Thanks to #Maciej Kowalski I have noted that I was using the when condition versus a real class and not versus a mocked one.
So I added the annotation #Spy to the Service in the Unit Test. The code of the new test is:
#Mock
Dao dao;
#InjectMocks
#Spy
Service service;
#Before
MockitoAnnotations.initMocks(this);
#Test(expected=MyException.class)
public void shouldReturnMyException throws MyException {
doThrow(new MyException()).when(service.serviceMethod());
}
But now I have this problem:
the expected exception is MyException but was

You would have to #spy the Service as i assume you are using it as the instance with real implementation being invoked.
So try this:
#InjectMocks
#Spy
Service service;
#Test(expected=MyException.class)
public void shouldReturnMyException throws MyException {
doThrow(new MyException()).when(service).serviceMethod();
}
remember to start with doXX when mocking a #Spy.
Update
If you want to mock the dao call (which is a better test candidate..) you would have to make some changes if you only want to use plain Mockito.
If the dao is not your instance dependency then you will have to change your prod method:
public class Service {
public Object serviceMethod() throws MyException {
Dao dao = getDaoInstance();
try {
return dao.dao_method();
} catch (ExceptionFromDaoMethod e) {
throw (new MyException());
}
}
Dao getDaoInstance(){
return new Dao();
}
}
And your test class should me more or less like this:
#Mock
Dao dao;
#Spy
Service service;
#Test(expected=MyException.class)
public void shouldReturnMyException throws MyException {
doReturn(dao).when(service).getDaoInstance();
when(dao.dao_method()).thenThrow(new ExceptionFromDaoMethod());
service.serviceMethod();
}

Finally I found a solution: create unit tests for a Spring project is more challenging than I expected (at least using only Mockito Framework), so I found a solution using also some Spring Annotations
So this is my new working test:
#MockBean
Dao dao;
#Autowired
Service service;
#Test(expected=MyException.class)
public void shouldReturnMyException throws MyException {
when(dao.dao_method()).thenThrow(new ExceptionFromDaoMethod());
}

Related

Mockito cannot inject mocks for #Async method

I have a class
#EnableAsync
class A {
#Autowired
private SomeService someService;
#Async
public void someMethod() {
this.someSerivice.call();
}
}
class ATest {
#Before
public void before() {
MockitoAnnotations.init(this)
}
#Autowired
#InjectMocks
private A a;
#Mock
private SomeService someService;
#Test
public void someTest() {
}
}
In the above example, someService should be mocked by Mockito. However, due to presence of #Async, it goes not get mocked and I recieve the actual instance.
Has anyone faced this? Any solutions?
You can set the mock manually like below in your test case
ReflectionTestUtils.setField(a, "someService", someService);

Method mocked remains for other tests as well

I am writing JUnits for my Spring controller. In one of the cases, I am testing the Exception catched within the controller. I have mocked the service to throw a RuntimeException. Other test checks the successful output.
Individually both tests work, but when I execute both together, if the first test executes first, then the second test too throw RuntimeException. Is there anything that I need to deregister the mocked method?
(Please ignore the syntax)
class UserController {
#Autowired
UserService service;
#RequestMapping("...")
public ResponseEntity getUser(){
try{
User user = service.getUserAttributes();
return new ResponseEntity(user, HttpStatus.OK);
}
catch(Exception e){
return new ResponseEntity("Eror", HttpStatus.BAD_REQUEST);
}
}
}
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration(classes=TestConfig.class)
public class UserControllerDocumentation {
#Autowired
private WebApplicationContext webApplicationContext;
private RestDocumentationResultHandler document;
private MockMvc mockMvc;
#Rule
public final RestDocumentation restDocumentation = new RestDocumentation("target/generated-snippets");
#Autowired
UserService userService;
#Before
public void setUp(){
this.document = document("{method-name}", preprocessRequest(prettyPrint()),
preprocessResponse(prettyPrint()));
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.webApplicationContext)
.apply(documentationConfiguration(this.restDocumentation)).alwaysDo(document)
.build();
}
#Test
public void textError() throws Exception {
when(userService.getUserAttributes(anyInt())).thenThrow(new RuntimeException());
this.mockMvc.perform(get("/user/xxx")
.accept("application/xml"))
.andExpect(status().isBadRequest());
}
#Test
public void textUser() throws Exception {
when(userService.getUserAttributes(anyInt())).thenReturn(mockUserObj);
this.mockMvc.perform(get("/user/10")
.accept("application/xml"))
.andExpect(status().isOk());
}
Maybe you should reset mocked object before you run each test.
You can do it using reset method.
Here are more details
http://site.mockito.org/mockito/docs/current/org/mockito/Mockito.html#17
To run this before each class you can use #Before annotation.
I don't know how #Autowired works, but obviously: your class has one instance of userService; and your textError() testcase configures that to throw an exception. And when your second test runs, that "behavior specification" is still in place and throws on you.
Thus, I see two options:
a) you use your "after" or "before" methods to reset the userService
b) each test method gets its own userService
Turns out that I need to return something for the mocked method even after throwing an exception. Added thenReturn(null) after thenThrow
#Test
public void textError() throws Exception {
when(userService.getUserAttributes(anyInt())).thenThrow(new RuntimeException()).thenReturn(null);
this.mockMvc.perform(get("/user/xxx")
.accept("application/xml"))
.andExpect(status().isBadRequest());
}

Mocking Spring bean's method behavior breaks aspects

I searched SO and found bunch of other questions that looked similar but not exactly, so I'll ask another one.
I have Spring application and say I created custom aspect (looking for CatchMe annotation) to log exceptions in a specific way. I want to test the aspect by mocking the behavior of one of my Spring #Service class's method so it throws exception when it is called. Then in another method, annotated with my custom annotation #CatchMe, I call the first method. What I expect to happen is the exception to get logged. Unfortunatelly the exception is thrown but the aspect is not triggered. So how can I make the aspect to get triggered in this test using Mockito?
Note: I've checked those (plus a bunch more):
Unit testing Spring #Around AOP methods
Spring Aspect not triggered in unit test
Spring: cannot inject a mock into class annotated with the #Aspect annotation
but most of them are Controller related and not Service related and I want to test only the service.
The Test
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {BeanConfig.class})
public class MyServiceTest {
#Autowired
#InjectMocks
private MyService service;
#Mock
private MyServiceDependency serviceDep;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
ReflectionTestUtils.setField(service, "serviceDep", serviceDep);
}
#Test
public void test() {
when(serviceDep.process()).thenAnswer(new Answer<Object>() {
#Override
public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
throw new Exception("Sample message.");
}
});
service.execute();
}
}
Services
#Service
public class MyService {
#Autowired
private MyServiceDependency serviceDep;
#CatchMe
public void execute() {
serviceDep.process();
}
}
#Service
public class MyServiceDependency {
public Object process() {
// may throw exception here
}
}
Configuration and Aspect
#Configuration
#EnableAspectJAutoProxy
#ComponentScan(basePackages = {"com.example.services"})
public class BeanConfig { .. }
#Aspect
#Component
public class CatchMeAspect {
#Around("#annotation(CatchMe)")
public Object catchMe(final ProceedingJoinPoint pjp) throws Throwable {
try {
pjp.proceed();
} catch (Throwable t) {
// fency log
}
}
}
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface CatchMe {}
EDIT: The functionality works but I want to verify it with the test.
Actually it is working as expected, however you are running in a side effect of proxy based AOP, especially class based proxies in this case.
Currently you are setting the field on the proxy and not on the actual object inside the proxy. Which is what you actually want. To obtain the actual instance use AopTestUtils.getUltimateTargetObject and then use that in the ReflectionTestUtils.setField method.
#Autowired
#InjectMocks
private MyService service;
#Mock
private MyServiceDependency serviceDep;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
MyService serviceToInject = AopTestUtils.getUltimateTargetObject(service);
ReflectionTestUtils.setField(serviceToInject, "serviceDep", serviceDep);
}
However I think that approach is wrong, when you start messing around like this there is a better way. Simply use Spring to inject the mock. Create a specific #Configuration class for this test case. Make it a internal public static class and for the dependency add a mocked #Bean.
#Configuration
#Import(BeanConfig.class)
public static class TestBeanConfig {
#Bean
public MyServiceDependency myServiceDependency() {
return Mockito.mock(MyServiceDependency.class);
}
}
Now in your test class you can simply #Autowire both beans and not need to use reflection or whatever to set dependencies.
#RunWith(SpringJUnit4ClassRunner.class)
public class MyServiceTest {
#Autowired
private MyService service;
#Autowired
private MyServiceDependency serviceDep;
#Test
public void test() {
when(serviceDep.process()).thenAnswer(new Answer<Object>() {
#Override
public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
throw new Exception("Sample message.");
}
});
service.execute();
}
}
Which will take care of the correct dependencies.
I had the same problem as #nyxz and this is intentional, see https://github.com/spring-projects/spring-boot/issues/7243.
Inspired by #M. Deinum following solution worked for me with Spring Boot 2.3.4.RELEASE and JUnit 5.
We will just provide a mocked bean without #MockedBean
#SpringBootTest
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
class MyServiceTest {
#Autowired
private MyService service;
#Test
public void test() {
service.execute();
}
static class TestBeanConfig {
#Bean
#Primary
public MyServiceDependency myServiceDependency() {
MyServiceDependency myServiceDependency = Mockito.mock(MyServiceDependency.class)
// Add behavior of mocked bean here
return myServiceDependency;
}
}
}

Mock class inside REST controller with Mockito

I have a spring-boot application which exposes a REST interface via a controller. This is an example of my controller:
#RestController
public class Controller {
#Autowired
private Processor processor;
#RequestMapping("/magic")
public void handleRequest() {
// process the POST request
processor.process();
}
}
I am trying to write unit tests for this class and I have to mock the processor (since the processing takes very long time and I am trying to avoid this step during testing the controller behavior). Please note, that the provided example is simplified for the sake of this question.
I am trying to use the mockito framework for this task:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = App.class)
#WebAppConfiguration
#ActiveProfiles("test")
public class ControllerTest {
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Before
public void setUp() throws Exception {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
Processor processor = Mockito.mock(Processor.class);
ReflectionTestUtils.setField(Controller.class, "processor", processor);
}
#Test
public void testControllerEmptyBody() throws Exception {
this.mockMvc.perform(post("/magic")).andExpect(status().isOk());
}
}
However, this fails with
java.lang.IllegalArgumentException: Could not find field [processor] of type [null] on target [class org.company.Controller]
at org.springframework.test.util.ReflectionTestUtils.setField(ReflectionTestUtils.java:112)
...
Could please someone give me a hint, how this mock could be injected in my controller?
Shouldn't you be passing an instance to set the field on, rather than the class, e.g.:
...
#Autowired
private Controller controller;
...
#Before
public void setUp() throws Exception {
...
Processor processor = Mockito.mock(Processor.class);
ReflectionTestUtils.setField(controller, "processor", processor);
}
I think that you can inject directly the mock like:
#InjectMocks
private ProcessorImpl processorMock;
And remove this line:
ReflectionTestUtils.setField(Controller.class, "processor", processor);
See Injection of a mock object into an object to be tested declared as a field in the test does not work using Mockito?
Rework your controller to use constructor injection instead of field injection. This makes the dependency explicit and makes your test setup drastically simpler.
#Before
public void setUp() throws Exception {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
Processor processor = Mockito.mock(Processor.class);
//This line should be added to perform mock for Processor.
Mockito.when(processor.process()).thenReturn(<Your returned value>);
//ReflectionTestUtils.setField(Controller.class, "processor", processor);
}
In above put the your returned value for "Your returned value" and in test use this value to verify your output.
You can remove servlet context class in SpringApplicationConfiguration and mock servlet context. There is no need for injection of WebApplicationContext and ReflectionTestUtils.
Basically, your code should look something like this:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = MockServletContext.class)
#WebAppConfiguration
#ActiveProfiles("test")
public class ControllerTest {
#InjectMocks
private MyController controller;
#Mock
private Processor processor;
private MockMvc mockMvc;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
}
#Test
public void testControllerEmptyBody() throws Exception {
when(proessor.process()).thenReturn(<yourValue>);
this.mockMvc.perform(post("/magic")).andExpect(status().isOk());
verify(processor, times(<number of times>)).process();
}
}
Processor will be mocked and mock will be injected into controller.

autowired dependency not getting mocked while executing test case with junit and mockito

I am using Junit4 and Mockito for test cases, in the following code dbprop.getProperty("config") is throwing a NullPointerException because dbProp is null. Please help me out why it was not mocked?
public abstract class BaseClass {
#Autowired
protected DBproperties dbprop;
}
public class SampleClass extends BaseClass {
#Autowired
private OrderService orderService;
valdiateOrder(String input) {
String config = dbprop.getProperty("config");
}
}
public class TestSampleClass {
#InjectMocks
SampleClass sampleClass;
#Mock
private OrderService orderService;
#Test
public void testValidateOrder() {
DBproperties dbprop = mock(DBproperties .class);
when(dbprop.getProperty("config")).thenReturn("xxxx");
assertNotNull(SampleClass.valdiateOrder("xxx"));
}
}
Your dbprop mock has not been injected into sampleClass, you need to add:
#Mock
private DBproperties dbprop;
Then remove the dbprop mock creation from your test method:
#Test
public void testValidateOrder() {
// DBproperties dbprop = mock(DBproperties .class); <-- removed
when(dbprop.getProperty("config")).thenReturn("xxxx");
assertNotNull(SampleClass.valdiateOrder("xxx"));
}
Next, to ensure mocks are injected when using the #InjectMocks annotations you need to either add the following runner:
#RunWith(MockitoJUnitRunner.class)
public class TestSampleClass {
...
Or call the following in a #Before method:
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
See the MockitoAnnotations and MockitoJUnitRunner JavaDocs for more information on the two approaches.
You can annotate your Object with #Mock, so its look like this
#Mock
DBproperties dbProperties;#Before public void init(){ MockitoAnnotations.initMocks(this);
}

Categories