How to wire dependencies in Spring tests

hochas Source

I'm having a hard time figuring out how to write Unit tests in Spring.

This is the method I'm trying to test:

@Service
public class ActionRequestHandler {

    @Autowired
    private Vertx vertx;

    @Autowired
    private InventoryService inventoryService;

    @Autowired
    private RequestSerializationWrapper requestWrapper;

    @Autowired
    private ProducerTemplate producer;

    @EventListener
    @Transactional
    public void registerConsumer(ApplicationReadyEvent event) {
        EventBus eb = vertx.eventBus();

        eb.consumer("bos.admin.wui.action", (Message<String> msg) -> {
            handleIncomingRequest(msg);
        });
    }
    // ...
}

So far I have tried creating a configuration inside my test class, like this:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader = AnnotationConfigContextLoader.class)
public class ActionRequestHandlerTest {

    @Configuration
    static class ContextConfiguration {

        @Bean
        @Primary
        public Vertx vertx() {
            return Mockito.mock(Vertx.class);
        }

        @Bean
        @Primary
        public InventoryService inventoryService() {
            return Mockito.mock(InventoryService.class);
        }

        @Bean
        @Primary
        public RequestSerializationWrapper requestWrapper() {
            return new RequestSerializationWrapper();
        }
    }

    @Autowired
    private Vertx vertx;

    @Autowired
    private InventoryService inventoryService;

    @Autowired
    private RequestSerializationWrapper requestWrapper;

    @Autowired
    private ActionRequestHandler systemUnderTest;

    @Test
    public void registerConsumer_shouldRegisterVertxEventBusConsumer() {
        EventBus eventBusMock = Mockito.mock(EventBus.class);
        Mockito.when(vertx.eventBus()).thenReturn(eventBusMock);

        systemUnderTest.registerConsumer(null);

        Mockito.verify(eventBusMock.consumer(Matchers.anyString()), Mockito.times(1));
    }
}

However, this seems to try to resolve every dependency inside InventoryService instead of mocking the entire class. The above configuration gives me this error when I run:

Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private admin.messaging.converters.XmlToEntityConverter admin.persistence.service.InventoryService.entityConverter; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [admin.messaging.converters.XmlToEntityConverter] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

I have also tried using a profile as suggested here. The configuration class looks the same:

@Profile("test")
@Configuration
public class ActionRequestHandlerTestConfiguration {

    @Bean
    @Primary
    public Vertx vertx() {
        return Mockito.mock(Vertx.class);
    }

    @Bean
    @Primary
    public InventoryService inventoryService() {
        return Mockito.mock(InventoryService.class);
    }

    @Bean
    @Primary
    public RequestSerializationWrapper requestWrapper() {
        return new RequestSerializationWrapper();
    }
}

The test is set up a bit differently with the following annotations instead:

@ActiveProfiles("test")
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = ActionRequestHandler.class)
public class ActionRequestHandlerTest {
    // ...
}

But this instead gives me an error that Vertx can't be wired:

Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private io.vertx.core.Vertx admin.messaging.request.ActionRequestHandler.vertx; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [io.vertx.core.Vertx] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

How can I get this to work? Where am I going wrong?

javaspringmockingmockito

Answers

answered 3 months ago Stack Victor #1

Try

@ContextConfiguration("/test-context.xml")
@RunWith(SpringJUnit4ClassRunner.class)

This tells Junit to use the test-context.xml file in the same directory as your test. This file should be similar to the real context.xml you're using for spring, but pointing to test resources, naturally.

answered 3 months ago Yogesh Badke #2

You don't need whole spring context to write unit test for ActionRequestHandler. You should use MockitoJunitRunner instead and do mocking on the dependencies.

@RunWith(MockitoJunitRunner.class)
public class ActionRequestHandlerTest {

    @Mock
    private Vertx vertx;

    @Mock
    private InventoryService inventoryService;

    @Mock
    private RequestSerializationWrapper requestWrapper;

    @Mock
    private ProducerTemplate producer;

    @InjectMocks
    private ActionRequestHandler actionRequestHandler;

    @Test
    public void testRegisterConsumer() {
      .... Your code to test ActionRequestHandler#registerConsumer will go here....
    }
}

You can read more about it here.

comments powered by Disqus