Spring boot 2.0.5.RELEASE - sleuth and mockito - spring-boot

I have tried to sort this out for a week, but no luck at all. The issue is with the unit tests.
This is the class that I am trying to test:
import brave.Span;
import brave.Tracer;
#Service
public class InternetBackEndRestClient {
#Autowired
private Tracer tracer;
public PasswordJwtResponse generatePassworJwt(PasswordJwtRequest passwordJwtRequest, String traceId) throws LogonProxyException {
log.info("{\"Starting method\": \"generatePassworJwt\", \"input\": {} }", passwordJwtRequest);
Span newSpan = tracer.nextSpan().name("spanPasswordJwtResponse");
...
}
}
How can I do the unit test? Brave.Tracer is a final class so that I cannot mock it. Is there anyway to set up a context? or mock Tracer?
#RunWith(MockitoJUnitRunner.class)
public class InternetBackEndRestClientTest {
#InjectMocks
private InternetBackEndRestClient internetBackEndRestClient;
#Mock
private Tracer tracer;
#Test
public void generatePassworJwt_test() {
internetBackEndRestClient.generatePassworJwt(...);
....
}
}
Could anyone help me please?

Here is the solution that worked for me:
#RunWith(MockitoJUnitRunner.class)
public class InternetBackEndRestClientTest {
private static final String TRACEID = "12345678901234567890123456789012";
#InjectMocks
private InternetBackEndRestClient internetBackEndRestClient;
#Mock
private Tracer tracer;
#Mock
private Span span;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
when(tracer.nextSpan()).thenReturn(span);
when(tracer.nextSpan().name("spanPasswordJwtResponse"))
.thenReturn(span);
when(span.start()).thenReturn(span);
Tracing tracing = Tracing.newBuilder().build();
doReturn(tracing.tracer().withSpanInScope(span))
.when(tracer).withSpanInScope(span);
doNothing().when(span).finish();
...
}
...
}

You can manually set the span and trace id using TraceContext.newBuilder() in a test and past the Tracer into the class being tested.
Tracer tracer = Tracing.newBuilder().build().tracer();
TraceContext ctx = TraceContext.newBuilder().traceId(10L).spanId(10L).build();
Span span = tracer.toSpan(ctx);
tracer.withSpanInScope(span);
This might be a bit lighter than mocking the Tracer class

Your example isn't complete so it's hard to identify everything that's not quite right, but one thing is that #MockBean will only work if you're using Spring Boot testing's infrastructure. That means that you need to be using SpringRunner to run the test and you also have to have enabled #MockBean support. The most common way to do that is with #SpringBootTest:
#SpringBootTest
#RunWith(SpringRunner.class)
public class InternetBackEndRestClientTest {
// …
}
You can read more about #MockBean in the Spring Boot reference documentation.

Related

Spring Boot JUnit tests fail with Status expected:<200> but was:<404>

For some time I've been struggling to make JUnit tests for my rest controller. For some reason, every time I try to run them I get the error Status expected:<200> but was:<404>. Here is my controller:
#RestController
#RequestMapping("/travels")
#RequiredArgsConstructor
public class TravelController {
private final TravelService travelService;
private final TravelOutputDtoMapper travelOutputDtoMapper;
#GetMapping
public List<TravelOutputDto> getAll() {
List<Travel> travels = travelService.getAll();
return travels.stream()
.map(travelOutputDtoMapper::travelToTravelOutputDto)
.collect(Collectors.toList());
}
}
And here is my test:
#ExtendWith(SpringExtension.class)
#WebMvcTest(controllers = TravelController.class)
#ContextConfiguration(classes = {
TravelOutputDtoMapper.class,
TravelOutputDtoMapperImpl.class
})
class TravelControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private TravelService travelService;
#Autowired
private TravelOutputDtoMapper travelOutputDtoMapper;
#Test
void testGetAll() throws Exception {
List<Travel> travels = mockTravelList();
Mockito.when(travelService.getAll()).thenReturn(travels);
mockMvc.perform(get("/travels"))
.andExpect(status().isOk());
}
private List<Travel> mockTravelList() {
// Dummy travel list
}
}
I think the reason is connected with TravelOutputDtoMapper as if I remove it from the controller and don't try to inject it the tests are passing, but I cannot find any information why it is doing it. The autowired mapper has an instance and works just fine.
Here is the Mapper:
#Mapper(componentModel = "spring")
public interface TravelOutputDtoMapper {
#Mapping(target = "from", source = "entity.from.code")
#Mapping(target = "to", source = "entity.to.code")
TravelOutputDto travelToTravelOutputDto(Travel entity);
}
The #ContextConfiguration annotation is used for a different purpose:
#ContextConfiguration defines class-level metadata that is used to determine how to load and configure an ApplicationContext for integration tests.
Using Spring Boot and #WebMvcTest there's no need to manually specify how to load the context. That's done for you in the background.
If you'd use this annotation, you'd specify your main Spring Boot class here (your entry-point class with the #SpringBootApplication annotation).
From what I can see in your test and your question is that you want to provide an actual bean for the TravelOutputDtoMapper, but mock the TravelService.
In this case, you can use #TestConfiguration to add further beans to your sliced Spring TestContext:
// #ExtendWith(SpringExtension.class) can be removed. This extension is already registered with #WebMvcTest
#WebMvcTest(controllers = TravelController.class)
class TravelControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private TravelService travelService;
#Autowired
private TravelOutputDtoMapper travelOutputDtoMapper;
#TestConfiguration
static class TestConfig {
#Bean
public TravelOutputDtoMapper travelOutputDtoMapper() {
return new TravelOutputDtoMapper(); // I assume your mapper has no collaborators
}
}
// ... your MockMvc tests
}

Cannot inject #Service in Unit Test in SpringBoot project

i have a #Service that I am trying to mock in an Unit Test but i get a null value so far. In the application class I specify what are the scanBasePackages. Do I have to do this in a different way? Thanks.
This is my service class that implements an interface:
#Service
public class DeviceService implements DeviceServiceDao {
private List<Device> devices;
#Override
public List<Device> getDevices(long homeId) {
return devices;
}
}
This is my unit test.
public class SmartHomeControllerTest {
private RestTemplate restTemplate = new RestTemplate();
private static final String BASE_URL = “..”;
#Mock
private DeviceService deviceService;
#Test
public void getHomeRegisteredDevices() throws Exception {
Device activeDevice = new DeviceBuilder()
.getActiveDevice(true)
.getName("Alexa")
.getDeviceId(1)
.getHomeId(1)
.build();
Device inativeDevice = new DeviceBuilder()
.getInactiveDevice(false)
.getName("Heater")
.getDeviceId(2)
.getHomeId(1)
.build();
UriComponentsBuilder builder = UriComponentsBuilder
.fromUriString(BASE_URL + "/1/devices");
List response = restTemplate.getForObject(builder.toUriString(), List.class);
verify(deviceService, times(1)).getDevices(1);
verifyNoMoreInteractions(deviceService);
}
You have to use a Spring test runner if you want to load and use a Spring context during tests execution.
You don't specify any runner, so it uses by default the runner of your test API. Here is probably JUnit or TestNG (the runner using depends on the #Test annotation specified).
Besides, according to the logic of your test, you want to invoke the "real"
REST service :
List response = restTemplate.getForObject(builder.toUriString(),
List.class);
To achieve it, you should load the Spring context and load the Spring Boot container by annotating the test with #SpringBootTest.
If you use a Spring Boot context, to mock the dependency in the Spring context, you must not use #Mock from Mockito but #MockBean from Spring Boot.
To understand the difference between the two, you may refer to this question.
Note that if you are using the #SpringBootTest annotation, a TestRestTemplate is automatically available and can be autowired into your test.
But beware, this is fault tolerant. It may be suitable or not according to your tests.
So your code could look like :
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class SmartHomeControllerTest {
private static final String BASE_URL = “..”;
#Autowired
private TestRestTemplate restTemplate;
#MockBean
private DeviceService deviceService;
#Test
public void getHomeRegisteredDevices() throws Exception {
...
}
As a side note, avoid using raw type as List but favor generic type.
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = NotificationApplication.class)
public class EmailClientImplTest {
...
}
And also add the needed properties/configs in
/src/test/resources/application.yml
Good luck!
I figured it out, I am using Mockito and used that to annotate my test class. This allowed me to get a mock of the service class that i am trying to use.
#RunWith(MockitoJUnitRunner.class)
public class SmartHomeControllerTest {..
#Mock
private DeviceService deviceService;
}
Try with #InjectMock instead of #Mock
You should run your test with spring boot runner

Unit Testing a class with ServiceLocatorFactoryBean Autowired

I have a Interface which is registered as part of ServiceLocatorFactoryBean. The main purpose of this Interface is to act as a factory.
http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/factory/config/ServiceLocatorFactoryBean.html
I have "autowired" this Interface in various classes, that I want to test now with Mockito.
The issue is Mockito doesn't support interfaces. How can inject a mock of this interface in the class I am testing.
The only alternative I see is to run the test using SpringJunitRunner and providing an Application Context which has the bean configurations. But this is too verbose.
I take it you'd like to spy on the implementation that Spring generated for your interface?! That's close to impossible to achieve with what you have so far... However there are at least the following alternatives below.
Suppose we have the following setup:
public interface MyService {
String doIt();
}
#Component
public static class ServiceConsumer {
#Autowired
private MyService service;
public String execute() {
return service.doIt();
}
}
0) Later edit: while roaming around, I found that it may be possible to spy and even replace an autowired field with a mock, and fairly easy too, using Springockito-annotations.
#RunWith(SpringJUnit4ClassRunner.class)
#ComponentScan
#ContextConfiguration(loader = SpringockitoAnnotatedContextLoader.class, classes = {SpringockitoConsumerTest.class})
public class SpringockitoConsumerTest {
#WrapWithSpy(beanName = "myService")
#Autowired
private MyService mockService;
#Autowired
private ServiceConsumer consumer;
#Test
public void shouldConsumeService() {
assertEquals("allDone", consumer.execute());
verify(mockService).doIt();
}
}
If Springockito-annotations is out of the question, please see the 2 original suggestions below
1) You could just create your mock of the interface and auto-inject it Mockito in your bean. This is the simplest solution (I could think of at the time of writing) but it does not ensure that the #Autowired annotation was not forgotten in the consumer (perhaps a dedicated test could be added?!):
public class AutoInjectMocksConsumerTest {
#Mock
private MyService serviceMock;
#InjectMocks
private ServiceConsumer consumer = new ServiceConsumer();
#Before
public void initMocks() {
MockitoAnnotations.initMocks(this);
when(serviceMock.doIt()).thenReturn("allDone");
}
#Test
public void shouldConsumeService() {
assertEquals("allDone", consumer.execute());
verify(serviceMock).doIt();
}
}
2) Alternatively as you also said, you could run it with the SpringJunitRunner making a minimum of effort to define and instantiate the necessary Spring context while also providing your own service mock. Albeit people may complain this solution is not that clean, I find it sufficiently elegant and it also validates that the #Autowired annotation was not forgotten in the consumer implementation.
#RunWith(SpringJUnit4ClassRunner.class)
#Configuration
#ComponentScan
#ContextConfiguration(classes = {SpringAutowiringConsumerTest.class})
public class SpringAutowiringConsumerTest {
#Autowired
private MyService mockService;
#Autowired
private ServiceConsumer consumer;
#Test
public void shouldConsumeService() {
assertEquals("allDone", consumer.execute());
verify(mockService).doIt();
}
#Bean
public MyService mockService() {
MyService serviceMock = mock(MyService.class);
when(serviceMock.doIt()).thenReturn("allDone");
return serviceMock;
}
}

Custom filter not injected in Spring integration test

I have an integration test that is designed to start my Spring Boot app:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = AppConfigCorrelationIdTestIt.class)
#WebIntegrationTest("server.port:0")
public class CorrelationIdTestIT {
Where the configuration class is:
#EnableConfigurationProperties
#ComponentScan
#EnableAutoConfiguration
#Configuration
public class AppConfigCorrelationIdTestIt {
In the app I have a defined custom servlet filter:
public class CorrelationHeaderFilter implements Filter {
But when testing my app I'm finding that the customer filter isn't instantiated and injected in to the filter chain. The only way round this I've found is to manually create it as a bean in AppConfigCorrelationIdTestIt, and then it works perfectly.
#Bean
public CorrelationHeaderFilter correlationHeaderFilter() {
return new CorrelationHeaderFilter();
}
Any ideas why the filter isn't picked up by Spring Boot when the application starts?
Thanks
Usually, in test classes possible to use DefaultMockMvcBuilder.addFilter(Filter filter, String... urlPatterns) when configured MockMvc. For example:
#WebAppConfiguration
#ContextConfiguration
public class AuthenticationTest {
private static final String TOKEN_HEADER = "X-Firebase-Auth";
final String url = "http://localhost:8080/authentication";
final Logger logger = LoggerFactory.getLogger(AuthenticationTest.class);
#Autowired
private WebApplicationContext webApplicationContext;
#Autowired
public Filter springSecurityFilterChain;
private MockMvc mockMvc;
#Before
public void setUp(){
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
.addFilter(springSecurityFilterChain).build();
}
...
}
I based on this answer

Spring HATEOAS Resource assembler is not instantiated in unit test

I am trying to write a unit test for a REST controller which generates HATEOAS links via Resource assembler class. Everything is OK in production, but with the unit test Resource assembler class is not being injected into the controller.
my resource assembler class is:
#Component
public class ModelResourceAssembler extends ResourceAssemblerSupport<Model, ModelResource> {
public ModelResourceAssembler() {
super(ModelRestController.class, ModelResource.class);
}
#Bean
public ModelResourceAssembler modelResourceAssembler(){
return new ModelResourceAssembler();
}
#Override
public ModelResource toResource(Model model) {
...
}
}
The controller is:
#Controller
#RequestMapping("/demo")
#ComponentScan(basePackages = {"com.foo.demo"} )
public class ModelRestController {
#Autowired
private ModelPersistenceHandler modelPersistenceHandler;
#Autowired
private ModelResourceAssembler modelResourceAssembler;
...
}
And the unit test:
#RunWith(MockitoJUnitRunner.class)
#ContextConfiguration(loader = AnnotationConfigContextLoader.class, classes= {ModelResourceAssembler.class, ModelRestController.class})
public class ModelRestControllerTest {
private MockMvc mockMvc;
#InjectMocks
private ModelRestController modelRestController;
#Mock
private ModelPersistenceHandler modelPersistenceHandler;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.standaloneSetup(modelRestController).build();
}
...
}
No matter what I do the ModelResourceAssembler instance is always null. Since the application is Spring Boot it does not have the WebCoonfig classes and autowired WebApplicationContext is always null, so I cannot (and really don't want to since I am running a unit test) instantiate MockMvc via webAppContextSetup
The solution ended up being quite simple: I needed to add one line to my test:
#Spy
private ModelResourceAssembler modelResourceAssembler;
And the bean was instantiated and properly wired
In your example you use #InjectMocks but don't declare a mock for ModelResourceAssembler. You don't get an instance out of nowhere.
You use the MockitoJUnitRunner.class. It has no idea of Spring beans. For testing Spring applications you rather want to use SpringJUnit4ClassRunner.class.
If i may suggest, if you use constructor injection for your controller then you can just mock the dependency and not need spring junit test runner stuff.

Resources