Hot questions for Using Mockito in integration testing

Top 10 Java Open Source / Mockito / integration testing

Question:

I'm working to test (via JUnit4 and Spring MockMvc) a REST service adapter using Spring-boot. The adapter simply passes along requests made to it, to another REST service (using a custom RestTemplate) and appends additional data to the responses.

I'd like to run MockMvc tests to perform controller integration tests, but want to override the RestTemplate in the controller with a mock to allow me to predefine the 3rd party REST response and prevent it from being hit during each test. I've been able to accomplish this by instantiating a MockMvcBuilders.standAloneSetup() and passing it the controller to be tested with the mock injected as listed in this post (and my setup below), however I am not able to do the same using MockMvcBuilders.webAppContextSetup().

I've been through a few other posts, none of which answer the question as to how this might be accomplished. I would like to use the actual Spring application context for the tests instead of a standalone to prevent any gaps as the application is likely to grow.

EDIT: I am using Mockito as my mocking framework and am trying to inject one of its mocks into the context. If this isn't necessary, all the better.

Controller:

@RestController
@RequestMapping(Constants.REQUEST_MAPPING_PATH)
public class Controller{

    @Autowired
    private DataProvider dp;    

    @Autowired
    private RestTemplate template;

    @RequestMapping(value = Constants.REQUEST_MAPPING_RESOURCE, method = RequestMethod.GET)
    public Response getResponse(
            @RequestParam(required = true) String data,
            @RequestParam(required = false, defaultValue = "80") String minScore
            ) throws Exception {

        Response resp = new Response();

        // Set the request params from the client request
        Map<String, String> parameters = new HashMap<String, String>();
        parameters.put(Constants.PARAM_DATA, data);
        parameters.put(Constants.PARAM_FORMAT, Constants.PARAMS_FORMAT.JSON);

        resp = template.getForObject(Constants.RESTDATAPROVIDER_URL, Response.class, parameters);

        if(resp.getError() == null){
            resp.filterScoreLessThan(new BigDecimal(minScore));
            new DataHandler(dp).populateData(resp.getData());
        }
        return resp;
    }
}

Test class:

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@SpringApplicationConfiguration(classes = MainSpringBootAdapter.class)
@TestPropertySource("/application-junit.properties")
public class WacControllerTest {

    private static String controllerURL = Constants.REQUEST_MAPPING_PATH + Constants.REQUEST_MAPPING_RESOURCE + compressedParams_all;
    private static String compressedParams_all = "?data={data}&minScore={minScore}";

    @Autowired
    private WebApplicationContext wac;

    private MockMvc mockMvc;

    @InjectMocks
    private Controller Controller;

    @Mock
    private RestTemplate rt;

    @Value("${file}")
    private String file;

    @Spy
    private DataProvider dp;

    @Before
    public void setup() throws Exception {
        dp = new DataProvider(file);    
        MockitoAnnotations.initMocks(this);
        this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
    }

    @Test
    public void testGetResponse() throws Exception {

        String[] strings = {"requestData", "100"};

        Mockito.when(
            rt.getForObject(Mockito.<String> any(), Mockito.<Class<Object>> any(), Mockito.<Map<String, ?>> any()))
            .thenReturn(populateTestResponse());

        mockMvc.perform(get(controllerURL, strings)
            .accept(Constants.APPLICATION_JSON_UTF8))
            .andDo(MockMvcResultHandlers.print());

        Mockito.verify(rt, Mockito.times(1)).getForObject(Mockito.<String> any(), Mockito.<Class<?>> any(), Mockito.<Map<String, ?>> any());

        }


        private Response populateTestResponse() {
            Response  resp = new Response();

            resp.setScore(new BigDecimal(100));
            resp.setData("Some Data");

            return resp;
    }
}

Answer:

Spring's MockRestServiceServer is exactly what you're looking for.

Short description from javadoc of the class:

Main entry point for client-side REST testing. Used for tests that involve direct or indirect (through client code) use of the RestTemplate. Provides a way to set up fine-grained expectations on the requests that will be performed through the RestTemplate and a way to define the responses to send back removing the need for an actual running server.

Try to set up your test like this:

@WebAppConfiguration
@ContextConfiguration(classes = {YourSpringConfig.class})
@RunWith(SpringJUnit4ClassRunner.class)
public class ExampleResourceTest {

    private MockMvc mockMvc;
    private MockRestServiceServer mockRestServiceServer;

    @Autowired
    private WebApplicationContext wac;

    @Autowired
    private RestOperations restOperations;

    @Before
    public void setUp() throws Exception {
        mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
        mockRestServiceServer = MockRestServiceServer.createServer((RestTemplate) restOperations);
    }


    @Test
    public void testMyApiCall() throws Exception {
        // Following line verifies that our code behind /api/my/endpoint made a REST PUT
        // with expected parameters to remote service successfully
        expectRestCallSuccess();

        this.mockMvc.perform(MockMvcRequestBuilders.get("/api/my/endpoint"))
            .andExpect(status().isOk());
    }

    private void expectRestCallSuccess() {
        mockRestServiceServer.expect(
            requestTo("http://remote.rest.service/api/resource"))
            .andExpect(method(PUT))
            .andRespond(withSuccess("{\"message\": \"hello\"}", APPLICATION_JSON));
    }


}

Question:

I have a java application (no Spring inside) that I want to test with an integration test.

My main use case is the main function that with a specified input do some things on the database and send some request to two different services, one SOAP and one REST.

Now I have a working JUnit configuration (splitted in unit and integration tests) + io.fabric8:docker-maven-plugin that use a docker image for the database during integration tests.

What I'm trying to do is to add a mock for these 2 services, in particular, the method that is used to call directly the external service.

The big problem is that I have this structure:

class A{
    Result mainFunction(Request r){
        ....
        B b = new B(params);
        b.logEvent(someParameters)
        ....
    }
}
class B{
    int logEvent(Object someParameters){
        ....
        NotifierHandler nh = new NotifierHandler(param1);
        nh.sendNotification(json);
        ....
    }
}

where I have:

class NotifierHandler{
    String sendNotification(Json j){
        ...
        [call to REST service with some parameters]
        ...
        ...
        [call to SOAP service with some parameters]
        ...
    }
}

What I need: call A.mainFunction(r) having, in the test environment, replaced the NotifierHandler with a FakeNotifierHandler and/or change the behaviour of the method sendNotification().

Actual problems: Using Mockito and PowerMock now I have the problem that I'm not able to change globally and directly the class NotifierHandler with FakeNotifierHandler. The same trying to changing the behaviour of the method.

In particular, what I need is to create a

class FakeNotifierHandler{
    String sendNotification(Json j){
        ...
        [save on an HashMap what I should send to the REST service]
        ...
        ...
        [save on another HashMap what I should send to the SOAP service]
        ...
    }
}

Reading all example that I tryed I saw only simple examples that change the return value of a method and not the behaviour of one method of one class used by another and another that I'm using as the start point of the integration test.

NOTE: probably there is a fast way to do this but I'm very new on this type of tests (Mockito, PowerMock,...) and I have found no example for this particular strange case.

EDIT: not similar to How to mock constructor with PowerMockito because I need to change the behaviour of the method, not only the return value.

Thanks a lot in advance


Answer:

I found a solution that works very well and it is very simple!

The solution is PowerMock (https://github.com/powermock/powermock) and in particular replace the creation of an instance of a class with another: https://github.com/powermock/powermock/wiki/mockito#how-to-mock-construction-of-new-objects

There is only one problem in my project and it is JUnit 5. PowerMock support JUnit 4 and for this reason, only for some tests of the solution are using it. In order to do this there is the needed to replace

import org.junit.jupiter.api.Test;

with

import org.junit.Test;

In order to use teh "whenNew()" methods I had extented the class that in tests must be replaced and I have overwritten only methods that are necessary for the integration test. The big benefit of this solution is that my code is untouched and I can use this approach also on old code without the risk of introducing regressions during the refactor of the code.

Regarding the code of a integration test, here an example:

import org.junit.jupiter.api.DisplayName;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

@RunWith(PowerMockRunner.class)
@PowerMockIgnore({"javax.crypto.*" }) // https://github.com/powermock/powermock/issues/294
@PrepareForTest(LegacyCoreNetworkClassPlg.class) // it is the class that contains the "new SOAPCallHelper(..)" code that I want to intercept and replace with a stub
public class ITestExample extends InitTestSuite {
    @Test
    @DisplayName("Test the update of a document status")
    public void iTestStubLegacyNetworkCall() throws Exception {

        // I'm using JUnit 4
        // I need to call @BeforeAll defined in InitTestSuite.init();
        // that works only with JUnit 5
        init();

        LOG.debug("IN stubbing...");
        SOAPCallHelperStub stub = new SOAPCallHelperStub("empty");
        PowerMockito.whenNew(SOAPCallHelper.class).withAnyArguments().thenReturn(stub);
        LOG.debug("OUT stubbing!!!");

        LOG.debug("IN iTestStubLegacyNetworkCall");
        ...
        // Here I can create any instance of every class, but when an instance of 
        // LegacyCoreNetworkClassPlg.class is created directly or indirectly, PowerMock
        // is checking it and when LegacyCoreNetworkClassPlg.class will create a new
        // instance of SOAPCallHelper it will change it with the 
        // SOAPCallHelperStub instance.
        ...
        LOG.debug("OUT iTestStubLegacyNetworkCall");
    }
}

Here the configuration of the pom.xml

    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <junit.jupiter.version>5.5.2</junit.jupiter.version>
    <junit.vintage.version>5.5.2</junit.vintage.version>
    <junit.platform.version>1.3.2</junit.platform.version>
    <junit.platform.engine.version>1.5.2</junit.platform.engine.version>
    <powermock.version>2.0.2</powermock.version>

    <!-- FOR TEST -->
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-api</artifactId>
        <version>${junit.jupiter.version}</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <scope>test</scope>
    </dependency>
    <!-- Only required to run tests in an IDE that bundles an older version -->
    <dependency>
        <groupId>org.junit.platform</groupId>
        <artifactId>junit-platform-launcher</artifactId>
        <version>${junit.platform.version}</version>
        <scope>test</scope>
    </dependency>
    <!-- Only required to run tests in an IDE that bundles an older version -->
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-engine</artifactId>
        <version>${junit.jupiter.version}</version>
        <scope>test</scope>
    </dependency>
    <!-- Only required to run tests in an IDE that bundles an older version -->
    <dependency>
        <groupId>org.junit.vintage</groupId>
        <artifactId>junit-vintage-engine</artifactId>
        <version>${junit.vintage.version}</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.junit.platform</groupId>
        <artifactId>junit-platform-engine</artifactId>
        <version>${junit.platform.engine.version}</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-params</artifactId>
        <version>${junit.vintage.version}</version>
        <scope>test</scope>
    </dependency>

    <dependency>
        <groupId>org.powermock</groupId>
        <artifactId>powermock-module-junit4</artifactId>
        <version>${powermock.version}</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.powermock</groupId>
        <artifactId>powermock-api-mockito2</artifactId>
        <version>${powermock.version}</version>
        <scope>test</scope>
    </dependency>

Question:

I'm experiencing following problem. I've got a spring boot test, where I inject and spy the mongoDbChannel bean. Then I try to start the normal workflow and verify if the method send is called on the bean.

@RunWith(SpringRunner.class)
@SpringBootTest(classes = {MongoAsBackupConfig.class},
        properties = {},
        webEnvironment = SpringBootTest.WebEnvironment.NONE)
public class MongoAsBackupConfigTest {
    @SpyBean(name = "mongoDbChannel")
    private QueueChannel mongoDbChannel;

    @Autowired
    private DirectChannel mongoDbWithFailoverChannel;

    @DirtiesContext
    @Test
    public void shouldUseFallbackForFullQueue() throws InterruptedException {
        IntStream.rangeClosed(1, BACKUP_QUEUE_CAPACITY + OVERFILLING_CLICK_COUNT).forEach(someNumber ->
            mongoDbWithFailoverChannel.send(MessageBuilder.withPayload(createPayload(someNumber)).build()));
        verify(mongoDbChannel, times(BACKUP_QUEUE_CAPACITY)).send(Mockito.any());
    }
}

As a result, I get the error message that any doesn't match to the concrete parameter value. However normally any means any value of param. What went wrong here?

Argument(s) are different! Wanted:
mongoDbChannel.send(
    <any>
);
-> at MongoAsBackupConfigTest.shouldUseFallbackForFullQueue(MongoAsBackupConfigTest.java:67)
Actual invocation has different arguments:
mongoDbChannel.send(
    GenericMessage [payload=Click(...), headers={id=0eaa2317-b1b5-604d-65c5-78da521cd585, timestamp=1509085945379}],
    10
);
-> at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:115)

EDITED: I'm using java 8. And I tried to use any(GenericMessage.class), any(Message.class) but it was the same effect.


Answer:

I assume you are using java 8 which means that when using Mockito.any(), the compiler will infer the type that has to be used based on the parameter type in the signature of send method.

That seems to be Message based on the method definition : send(Message<?> message)

What is actually passed is an instance of GenericMessage.

As I assume GenericMessage extends Message, then you can write your verify as follows:

verify(mongoDbChannel, times(BACKUP_QUEUE_CAPACITY))
   .send(Mockito.any(GenericMessage.class));

Update

There also seems to be an overloaded method send(Message<?> message, long timeout). Maybe this version gets called instead of the single arg one..

Question:

I'm using Mockito and I'm trying to inject a Mock CustomFileHandler into my REjercicioDAO class for testing purposes. The thing is, my test throws no exceptions, but it doesn't inject my mock object, the original @Autowired CustomFileHandler is not being substituted. Here's my code:

@Repository
public class REjercicioDAO extends ARHibernateDAO < REjercicio > implements IREjercicioDAO {

    @Autowired
    public ICustomFileHandler customFileHandler;

    ...

}

And here's my test:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = ATest.CONTEXT_CONFIGURATION)
public class REjercicioDAOTest extends ATest {

    @Mock private ICustomFileHandler customFileHandler;

    @Autowired
    @InjectMocks
    private IREjercicioDAO rEjercicioDAO;

    @Before
    public void before () {

        MockitoAnnotations.initMocks(this);

        ...

    }

Btw, entities work as expected and interfaces are correctly linked to the actual entities, I have tested that. How can I fix this?


Answer:

Here is a no answer. I could not give more because I am really sorry to see so many people use this awkward API relying on reflection while you could do things really clear for the readers of the class by explicitly setting the dependency.

The thing is, my test throws no exceptions, but it doesn't inject my mock object

Not surprising. This way of injecting the mock stays quiet even if no injection succeeds. From the InjectMocks javadoc (emphasis is not mine!) :

Mockito will try to inject mocks only either by constructor injection, setter injection, or property injection in order and as described below. If any of the following strategy fail, then Mockito won't report failure; i.e. you will have to provide dependencies yourself.

While Mockito does not report failure, I really discourage to use this API.

About your actual issue, look at that :

@Autowired
@InjectMocks
private IREjercicioDAO rEjercicioDAO;

You annotate the field with both Spring and Mockito annotation. Do you feel confortable with the order of their processing ? These come from two distinct libraries. I don't tell that it will never work (luck and random exists) but do you really think that it is robust ?

To achieve your requirement you could write something like that that does things in two explicit steps : - objects instantiation : mocking the dependency and inject the spring dependency - relationship set : between the mock dependency and the spring dependency

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = ATest.CONTEXT_CONFIGURATION)
public class REjercicioDAOTest extends ATest {

    @Mock 
    private ICustomFileHandler customFileHandler;

    @Autowired        
    private IREjercicioDAO rEjercicioDAO;

    @Before
    public void before () {    
        MockitoAnnotations.initMocks(this);
        // Set explicitly the fileHandler dependency
        rEjercicioDAO.setFileHandler(customFileHandler);         
    }
 }

Question:

I am using Mokito for testing and I have the following scenario. I am trying to test this code

public CartResponse processDeleteCartEntry(UUID cartId, Integer rowKey, JsonMessages messages)
        throws UnexpectedException {

    Cart cart = cartService.getById(cartId);

    CartResponse cartResponse = null;

    if (cart != null) {
        cartService.removeItem(cart, rowKey, messages);

        cartResponse = buildCartResponse(cart);
    }
    return cartResponse;
}

cartService.removeItem(cart, rowKey, messages); doesn't return anything (void) and this is my test case

@Test
public void testRemoveCartItem() throws UnexpectedException {
    Cart cart = getCart();

    //given
    given(cartService.getById(cart.getId())).willReturn(cart);

    //When
    CartResponse cartResponse = mobileAppCartHandler.processDeleteCartEntry(cart.getId(), 0, new JsonMessages());

    //Then
    assertNotNull(cartResponse);
    assertEquals(ResponseStatus.OK, cartResponse.getStatus());
    assertEquals(1, cartResponse.getEntries().size());

}

I do not want to make an actual call to remove an item but at the same time it should remove the item so that I can assert it. My cart has 2 items and it should be one after the removal. Should I be using when condition?


Answer:

For void methods, you need to stub the action first.

Mockito.doAnswer(invocation -> {
  // grab args and remove from cart
})
.when(cartService)  // mocked cartService
.removeItem(cart, rowKey, messages);  // You can use argumentMatchers here

Question:

I wrote simple spring boot application with Controller, Service and Business classes, but while writing integration test the mock method of Service is returning null

MockMainController

@RestController
public class MockMainController {

@Autowired
private MockBusiness mockBusiness;

@GetMapping("request")
public MockOutput mockRequest() {
    return mockBusiness.businessLogic(new MockInput());

      }

  }

MockBusiness

@Service
public class MockBusiness {

@Autowired
private MockService mockService;

public MockOutput businessLogic(MockInput input) {
    return mockService.serviceLogic(input);
    }
}

MockService

@Service
public class MockService {

@Autowired
private MockUtil mockUtil;

public MockOutput serviceLogic(MockInput input) {

    mockUtil.exchange(UriComponentsBuilder.fromUriString(" "), HttpMethod.GET, HttpEntity.EMPTY,
            new ParameterizedTypeReference<MockOutput>() {
            });
    return new MockOutput();

     }

 }

I'm trying to mock the MockService bean in application context using @MockBean

MockControllerTest

@SpringBootTest
@ActiveProfiles("test")
@Profile("test")
@RunWith(SpringJUnit4ClassRunner.class)
public class MockControllerTest {

@Autowired
private MockMainController mockMainController;

@MockBean
private MockService mockService;

@Test
public void controllerTest() {
    MockOutput output = mockMainController.mockRequest();
    given(this.mockService.serviceLogic(ArgumentMatchers.any(MockInput.class)))
            .willReturn(new MockOutput("hello", "success"));
    System.out.println(output);    //null
     }

 }

In the test method I created mock service bean using @MockBean I'm not having any error here but System.out.println(output); prints null


Answer:

You are getting null because of wrong statements order in your test method. You first call controller method and you get what's inside default @MockBean which is in this case null. Swap statement:

MockOutput output = mockMainController.mockRequest(); 

with

given(this.mockService.serviceLogic(ArgumentMatchers.any(MockInput.class)))
        .willReturn(new MockOutput("hello", "success")); 

and you will get expected result.

Question:

I'm using Mockito, DBUnit and HSQLDB to unit test my database code. I'm also writing integration tests of course.

I'm having trouble understanding how to inject a mocked DataSource into the system under test (class I'm testing). The DataSource is used for connection pooling, and therefore other classes can call a static method in the same class in order to retrieve an instance of this DataSource. This means that the DataSource is not injected into any constructors, anywhere, and so my tests don't have any constructors to inject the mocked DataSource into.

I'm getting around this by altering the logic of my real code to check if a private variable is null, and if so then use the injected DataSource (bad design since it's only needed for tests), otherwise it calls the static method to retrieve the connection pool's source (better design).

How do I inject a mocked DataSource into a class that doesn't have a constructor set up to accept it, because it can instead just call the static method to retrieve the dependency?

Class to Test

public DBConnection(DBSource dbSource) {   // <--- Constructor only required for test purposes :(
        this.dbSource = dbSource;
    }

    public final void createCompsDB() {
        Connection conn = null;
        Statement statement = null;
        try {
            if(dbSource==null){ 
                conn = DBSource.getInstance().getConnection();
            }else{
                conn = dbSource.getConnection(); /** Likely bad design, since dbSource is only NOT null for tests, so that you can inject the mocked datasource :(  */
            }
            statement = conn.createStatement();
            statement.executeUpdate("CREATE DATABASE placesdb");
            System.out.println("Database created...");
        } catch (SQLException e) {
              // ...
            }
        } finally {
            // Close Resources... 
        }
    }
 }

Test Class -- Test Passes

public class DBConnectionTest {
        final Statement statement = mock(Statement.class);
        final Connection connection = mock(Connection.class);
        final DBSource dataSource = mock(DBSource.class);

    @Before
    public void setUp() throws SQLException, IOException, PropertyVetoException {
        when(dataSource.getConnection()).thenReturn(connection);
        when(connection.createStatement()).thenReturn(statement);
    }

    @Test
    public void testCreateCompDBIfNotAlready() throws Exception {
        DBConnection dbConnection = new DBConnection(localDB, dataSource); /** This constructor is only needed for testing :( . How do I avoid it since all the classes I need to test don't require the dependency to be injected? */
        dbConnection.createCompsDB();    
        verify(statement).executeUpdate("CREATE DATABASE PLACES");
    }
}

DBSource.java

protected DBSource() throws IOException, SQLException, PropertyVetoException {
        ds = new BasicDataSource();
        ds.setDriverClassName("org.postgresql.Driver");
        ds.setUsername("user");
        ds.setPassword("pass");
        ds.setUrl("jdbc:postgresql://localhost:5432/placesdb");
    }

    public static DBSource getInstance() {   // <--- Static method means dependent classes don't need to accept injections
        if (datasource == null) {
            datasource = new DBSource();
            return datasource;
        } else {
            return datasource;
        }
    }

    public Connection getConnection() throws SQLException {
        return this.ds.getConnection();
    }
}

Answer:

Mocking of the static class methods may be done with PowerMockito. The test class should be something like this:

@RunWith(PowerMockRunner.class)
@PrepareForTest(DBSource.class)
public class DBConnectionTest {
    @Mock
    final Statement statement;
    @Mock
    final Connection connection;
    @Mock
    final DBSource dbsource;

    @Before
    public void setUp() throws SQLException, IOException, PropertyVetoException {
        PowerMockito.mockStatic(DBSource.class);
        when(DbSource.getInstance()).thenReturn(dbsource);
        when(dbsource.getConnection()).thenReturn(connection);
        when(connection.createStatement()).thenReturn(statement);
    }

    @Test
    public void testCreateCompDBIfNotAlready() throws Exception {
        DBConnection dbConnection = new DBConnection(localDB); // No test-only constructor anymore
        dbConnection.createCompsDB();    
        verify(statement).executeUpdate("CREATE DATABASE PLACES");
    }
}

You can read here more about mocking with PowerMock.

Question:

Can I somehow use doAnswer() when an exception is thrown?

I'm using this in my integration test to get method invocations and the test in configured the @RabbitListenerTest...

@RunWith(SpringRunner.class)
@SpringBootTest
public class MyIT {

@Autowired
private RabbitTemplate rabbitTemplate;

@Autowired
private MyRabbitListener myRabbitListener;

@Autowired
private RabbitListenerTestHarness harness;

@Test
public void testListener() throws InterruptedException {
  MyRabbitListener myRabbitListener = this.harness.getSpy("event");
  assertNotNull(myRabbitListener);

  final String message = "Test Message";
  LatchCountDownAndCallRealMethodAnswer answer = new LatchCountDownAndCallRealMethodAnswer(1);
  doAnswer(answer).when(myRabbitListener).event(message);

  rabbitTemplate.convertAndSend("exchange", "key", message);

  assertTrue(answer.getLatch().await(20, TimeUnit.SECONDS));
  verify(myRabbitListener).messageReceiver(message);
}

@Configuration
@RabbitListenerTest
public static class Config {
  @Bean
  public MyRabbitListener myRabbitListener(){
    return new MyRabbitListener();
  }
 }
}

It works ok but when I introduce an Exception being thrown, It doesn't i.e

This works

@RabbitListener(id = "event", queues = "queue-name")
  public void event(String message) {
    log.info("received message > " + message);
}

This doesn't

@RabbitListener(id = "event", queues = "queue-name")
  public void event(String message) {
    log.info("received message > " + message);
    throw new ImmediateAcknowledgeAmqpException("Invalid message, " + message);
}

Any help appreciated


Answer:

The LatchCountDownAndCallRealMethodAnswer is very basic

@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
    invocation.callRealMethod();
    this.latch.countDown();
    return null;
}

You can copy it to a new class and change it to something like

private volatile Exception exeption;

@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
    try {
        invocation.callRealMethod();
    } 
    catch (RuntimeException e) {
        this.exception = e;
        throw e;
    }
    finally {
        this.latch.countDown();
    }
    return null;
}

public Exception getException() {
    return this.exception;
}

then

assertTrue(answer.getLatch().await(20, TimeUnit.SECONDS));
assertThat(answer.getException(), isInstanceOf(ImmediateAcknowledgeAmqpException.class));

Please open a github issue; the framework should support this out-of-the-box.

Question:

I'm using mockito to return different values for the same function calls:

doAnswer(new Answer() {
  int counter = 0;

  @Override
  public Object answer(InvocationOnMock invocation) throws Throwable {
    if (counter == 0) {
        counter += 1;
      return object1;
    } else {
      return object2;
    }
  }
}).when(thing).thingFunction();

thingFunction is only called once for right now, however, on that first call, mockito starts self-invoking over and over (3-5 times) thus increasing this counter. No idea why this would be happening. Is this correct?


Answer:

Your code should be correct except there is a warning in your statement because Answer is a generic class. You should write new Answer<Object>() { //.. } (according to the returning type of your mocked method)

I wrote a Junit Test with Mockito 1.10.19 for clarification:

import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;

import static org.junit.Assert.*;

import org.junit.Test;

public class TestClass {


    Object object1 = new Object();
    Object object2 = new Object();

    class Thing{
        public Object thingFunction(){
            return null;
        }
    }

    @Test
    public void test(){
        Thing thing = Mockito.mock(Thing.class);
        Mockito.doAnswer(new Answer<Object>() {
              int counter = 0;

              @Override
              public Object answer(InvocationOnMock invocation) throws Throwable {
                if (counter == 0) {
                    counter += 1;
                  return object1;
                } else {
                  return object2;
                }
              }
            }).when(thing).thingFunction();

        assertEquals(object1, thing.thingFunction());
        assertEquals(object2, thing.thingFunction());
    }
}

Question:

I am trying to write an integration test for JMS service which looks like something like this.

 @JmsListener(destination = "mailbox", containerFactory = "myFactory")
 public void receiveMessage(Email message) throws InterruptedException {
    try {
        sendEmail(message);
    }catch (Exception e){
        LOGGER.log(Level.SEVERE,"Failed to deliver email",e);
        Thread.sleep(TimeUnit.SECONDS.toSeconds(Optional.of(retryInterval).orElse(5)));
        throw e;
    }
}

private void sendEmail(Email message){
    ...............
}

First of all, can I mock this some how? I tried mocking it, but when I send a message the spring boot application is calling the actual JMS bean not the mock one. Seems like this is not possible. Even if this is not possible, can I at least aoutowire the bean and somehow check if the receiveMessage method is being invoked. Furthermore, if it is being invoked, the sendEmail part should be faked so that it does not do any work. I have a few ideas such as creating a subclass for testing, but not happy with either of them. So wanted to if you can suggest me a better work around?


Answer:

One approach is to use different profiles for say development, integration test and production and annotate the different components and your integration test class accordingly.

@Component
@Profile("it")
public class MessageReceiverIT {

    @JmsListener(destination = "mailbox", containerFactory = "myFactory")
    public void receiveMessage(SimpleMessage email) {
        log.info("Integration test pretend to receive {}", email);
// (...)

This is the Integration test that uses the same Application class as the real Application, but if a message is received the MessageReceiverIT.receiveMessage() method will be invoked instead of the production component:

@RunWith(SpringRunner.class)
@SpringBootTest(classes=Application.class)
@ActiveProfiles("it")
public class JmsIntegrationTest {

    @Inject
    ConfigurableApplicationContext context;

    @Test
    public void testSend() throws Exception{
        JmsTemplate jmsTemplate = context.getBean(JmsTemplate.class);
        jmsTemplate.convertAndSend("mailbox", new SimpleMessage("it", "we need more IT"));
// (...)

Also check out Spring Boot Testing for alternative approaches such as the use of @TestConfiguration. I'm using Spring Boot in my examples, but there should be similar approaches if you have a none Spring Boot Application.