Hot questions for Using Mockito in spring integration

Top 10 Java Open Source / Mockito / spring integration

Question:

I am trying to mock the external call.

 ResponseEntity<?> httpResponse = requestGateway.pushNotification(xtifyRequest);

Answer:

You can instead use the type-unsafe method

doReturn(r).when(requestGateway.pushNotification(any(XtifyRequest.class)));

Or you can remove the type info while mocking

ResponseEntity r=new ResponseEntity(HttpStatus.ACCEPTED);
when(requestGateway.pushNotification(any(XtifyRequest.class))).thenReturn(r);

Question:

The example below works correct. The problem is that I do need @InjectMocks annotation. And when I replace SpringJUnit4ClassRunner.class with MockitoJUnitRunner.class everything breaks (bar = null instead of testValue).

How to fix?

//@RunWith(MockitoJUnitRunner.class) // not work (
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = FooTest.Config.class)
@TestPropertySource(properties = {
        "some.bar.value=testValue",
})
public class FooTest {

    @Value("${some.bar.value}")
    String bar;

    @Test
    public void testValueSetup() {
        assertEquals("testValue", bar);
    }

    @Configuration
    static class Config {

        @Bean
        public static PropertySourcesPlaceholderConfigurer propertiesResolver() {
            return new PropertySourcesPlaceholderConfigurer();
        }
    }
}

Answer:

You should be able to this combo of ClassRule and Rule to enable the same functionality the SpringRunner does.

    @ClassRule
    public static final SpringClassRule SPRING_CLASS_RULE = new SpringClassRule();

    @Rule
    public final SpringMethodRule springMethodRule = new SpringMethodRule();

and then use the MockitoRunner like normal.

Additionally, you could always just mock the dependencies directly with Mockito.mock etc. the annotations approach while slightly cleaner can lead to some annoying runtime issues if the test isn't simple, esp. in the case of @InjectMocks.

Curious why you need the Mockito runner within a SpringBoot project? You can use MockBeans to mock out Spring Beans when required. Smells like an XY Problem as I've never had to use the MockitoRunner.

Question:

I have a class which contains a few service activator methods as follows:

@MessageEndpoint
public class TestService {
    @ServiceActivator
    public void setComplete(Message<String> message){
        //do stuff
    }
}

In the integration flow, one of the channels call one of these methods:

@Bean
public TestService testService() {
    return new TestService();
}

@Bean
public IntegrationFlow testFlow() {
    return IntegrationFlows.from("testChannel")
            .handle("testService", "setComplete")
            .handle(logger())
            .get();
}

I'm writing a unit test for this flow and using Mockito for mcoking the service activator class:

@ContextConfiguration(classes = IntegrationConfig.class)
@RunWith(SpringJUnit4ClassRunner.class)
@DirtiesContext
public class AppTest {

    @Mock
    private TheGateway startGateway;

    @Mock
    private TestService testrvice;


    @Autowired
    @Qualifier("testChannel")
    DirectChannel testChannel;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
    }

    @Test()
    public void testMessageProducerFlow() throws Exception {

        Mockito.doNothing().when(startGateway).execute("test");
        startGateway.execute("test");
        Mockito.verify(startGateway).execute("test");
        TestChannel.send(new GenericMessage<>("test"));
        Mockito.verify(testService).setComplete(new GenericMessage<>("test"));

    }
}

When I don't mock the TestService, it executes the flow without issues. Any guideance on how to Mock the Service activator class would be helpful.

UPDATE: When I mock it (as shown in snippet above), it does not call the mocked object, instead executes the actual stuff, and the last line Mockito.verify(testService)... asserts that the mock testService was never called.


Answer:

First of all you misunderstood how Spring Test Framework works.

  1. @ContextConfiguration(classes = IntegrationConfig.class) loads the config as is without any modification and start an application context based on that config.

  2. According to the first condition your .handle("testService", "setComplete") uses testService() @Bean not @Mock

  3. Only after the test applicationContext startup all those @Mocks and @Autowireds start working.

In other words your mocking doesn't change anything in the original IntegrationConfig.

In the Framework with use reflection to retrieve some field of the particular bean to replace it with the mock. But it isn't so easy way.

I suggest you to distinguish the Integration and Service configuration and use two different classes for production and for testing. Something like this:

  1. The testService() @Bean must be moved from the IntegrationConfig to the new @Configuration class for production.

  2. The TestServiceConfig may look like this:

    @Bean
    public TestService testService() {
        return Mockito.mock(new TestService());
    }
    
  3. And finally your AppTest should be modified like this:

    @ContextConfiguration(classes = {IntegrationConfig.class, TestServiceConfig.class})
    ....
    @Autowired
    private TestService testrvice;
    

That's everything is just because the application context and unit test scopes are on the different levels.

Question:

I am trying to test a spring integration set up my unit test is as follows,

@ContextConfiguration(loader = AnnotationConfigContextLoader.class, classes = { SIContext2Config.class })
public class FileMoverTest extends GenericTest {

@Test
public void testFileMover() throws InterruptedException {
    Thread.sleep(1000);
    int total = 2;
    int n = 0;
    for (int i = 1; i <= total; i++) {
        @SuppressWarnings("unchecked")
        Message<OfferMessage> msg = (Message<OfferMessage>) hdfsReadyChannel.receive(2000);
        System.out.println("Message # " + i + " received:" + msg.getPayload().getOfferFile().getAbsolutePath());
        n++;
    }
    Assert.state(n == total);
}

The context class is as follows:

@Configuration
public class SIContext2Config { 

@Mock
private FsShell fsh;

@InjectMocks
@Mock
private MoveToHdfs fileMover;

@Bean
public Ingester ingester() {
    return new Ingester(fileMover);
}

@Mock
private Ingester ingester;

@Bean
public FilePickupHandler filePickupHandler() {
    return new FilePickupHandler();
}
}

Now, here is what I am trying to do: The Ingester bean has a method called handle(), inside which the MoveToHdfs object fileMover runs and calls move().

public OfferMessage handle(Message<OfferMessage> msg) {
    // get hive directory path
    String remotePath = msg.getPayload().getOfferComponent().getHiveDirectory();
    String localFile = msg.getPayload().getOfferFile().getAbsolutePath();
    LOGGER.debug("Moving file {} to remote path:{}", localFile, remotePath);

    if (!fileMover.move(localFile, remotePath, true)) {
            throw new SomeException();
    }

    return msg.getPayload();
}

I just want that to return true. But I can't figure out where to "stub" that, or how to stub that.


Answer:

Why don't rework that fileMover to the @Bean?

@Bean
public MoveToHdfs fileMover() {
    MoveToHdfs fileMover = Mockito.mock(MoveToHdfs.class);
    when(fileMover.move(anyString(), anyString(), anyBoolean())).thenReturn(true);
    return fileMover;
}

Question:

I want to write a JUnit test case and integration test case for Integration flow and GenericHandler.

I have gone through some articles but didn`t found anything useful.

Code Snippet 1

IntegrationFlows.from(() -> path, e -> e.poller(Pollers.fixedDelay(60, TimeUnit.SECONDS)))
.handle(Sftp.outboundGateway(sftpSessionFactory(), LS, "payload")
        .regexFileNameFilter(".*csv"))
.split()
.handle(Sftp.outboundGateway(sftpSessionFactory(), GET, "payload.remoteDirectory + payload.filename").options(STREAM).temporaryFileSuffix("_reading"))
.handle(readCsvData(), e -> e.advice(afterReadingCsv()))
.filter(this, "checkSuccess")
.enrichHeaders(h -> h
        .headerExpression(FileHeaders.RENAME_TO, "headers[file_remoteDirectory] + 'archive/' + headers[file_remoteFile]")
        .headerExpression(FileHeaders.REMOTE_FILE, "headers[file_remoteFile]")
        .header(FileHeaders.REMOTE_DIRECTORY, "headers[file_remoteDirectory]"))
.handle(Sftp.outboundGateway(sftpSessionFactory(), MV, "headers[file_remoteDirectory]+headers[file_remoteFile]").renameExpression("headers['file_renameTo']"))
.get();

Code Snippet 2

    public GenericHandler readCsvData() {
        return new GenericHandler() {
            @Override
            public Object handle(Object o, Map map) {
        }
       }
       }

It will be useful to get some direction for writing JUnit test cases for the above code snippets.


Answer:

You can do something like below

Solution code snippet 1: Mock the class and call the method then you can verify the interactions.

Solution code snippet 2 :

File file = ResourceUtils.getFile("classpath:someFile");
InputStream inputStream = new FileInputStream(file);
Map map = anyMap();
GenericHandler genericHandler = service.readCsvData();        
Object actual = genericHandler.handle(inputStream, map);
// Add some assertions here