spring-restdocs is not recognizing apply() - spring

I am trying to generate a Rest API documentation based on spring-restdocs
In following code I am getting a compile time error at apply()
The method apply(RestDocumentationMockMvcConfigurer) is undefined for the type DefaultMockMvcBuilder
#ContextConfiguration(locations = { "classpath:/testApplicationRestService.xml" })
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
public class CustomerControllerTest {
#Rule
public final RestDocumentation restDocumentation = new RestDocumentation(
"build/generated-snippets");
#Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
#Before
public void setUp() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
.apply(documentationConfiguration(this.restDocumentation))
.build();
}
}

Spring REST Docs requires Spring Franework 4.1 or later. The apply method is new in Spring Framework 4.1. The compile failure means that you have an earlier version on the classpath. You should update your pom.xml or build.gradle to ensure that you're using the required version.

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
}

Geting java.lang.IllegalStateException: Duplicate mock definition while using #MockBean in test case

I have one service class that I want to mock but while running the test I am Getting Caused by: java.lang.IllegalStateException: Duplicate mock definition [MockDefinition#482ba4b1 name = '', typeToMock = com.service.ThirdPartyService, extraInterfaces = set[[empty]], answer = RETURNS_DEFAULTS, serializable = false, reset = AFTER]
I have tried to create mock service using #MockBean at class level, field level, and used #Qualifier as well to resolve the issue
#Service
public class ThirdPartyService{
.......................
public String decrypt(String encryptedText) {
//third party SDK I am using
return Service.decrypt.apply(encryptedText);
}
.........
..............
}
#ComponentScan("com")
#PropertySource({"classpath:/api.properties", "classpath:/common.properties"})
#SpringBootConfiguration
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {Application.class}, webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
#Transactional
public class TestControllerTest extends IntegrationTest {
#MockBean
ThirdPartyService thirdPartyService;
#Before
public void initMocks(){
MockitoAnnotations.initMocks(this);
}
#Test
public void test() throws Exception {
when(ts.decrypt("encryptedText")).thenReturn("decryptedText")
Request req = Request.builder().name("name123").build();
//written performPost method in some other class
ResultActions action = performPost("/test", req);
action.andExpect(status().isOk());
}
}
public class IntegrationTest {
protected final Gson mapper = new Gson();
private MockMvc mvc;
#Autowired
private WebApplicationContext context;
public ObjectMapper objectMapper = new ObjectMapper();
#Before
public void setup() {
this.mvc = MockMvcBuilders.webAppContextSetup(context).apply(springSecurity()).build();
}}
When I am calling Thirdparty service decrypt method then it should return me decryptedText as a string. But getting duplicate mock definition error
I had the same issue.
Cause of this were test configuration file which was put somewhere else and it contained the mocked bean.
I have solved this by using #Autowired instead of #MockBean as this will result in autowiring the already mocked bean.
In my case the problem appeared after another dependency update and the reason was in the #SpringBootTest annotation referencing the same class twice:
#SpringBootTest(classes = {MyApplication.class, ApiControllerIT.class})
class ApiControllerIT extends IntegrationTestConfigurer {
// ...
}
#SpringBootTest(classes = {MyApplication.class, TestRestTemplateConfiguration.class})
public class IntegrationTestConfigurer {
// ...
}
I fixed it by removing #SpringBootTest annotation from the child class (ApiControllerIT).
In my case it was incorrect test class name that doesn't end with 'Test'.
If you have nested test classes try this:
#NestedTestConfiguration(OVERRIDE)
From Spring release notes: https://github.com/spring-projects/spring-framework/wiki/Upgrading-to-Spring-Framework-5.x#upgrading-to-version-53

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

ContentType not set in Mockito test

I am trying to get a unit test to work with Mockito and Spring MVC on a RESTful GET controller. Here is my test:
#RunWith(MockitoJUnitRunner.class)
#WebAppConfiguration
#ContextConfiguration(locations = {"/test-context.xml","/dataaccess-context.xml"})
public class FormControllerTest {
private MockMvc mockMvc;
#Autowired
FormImplBean formBean;
#Mock
private FormService formServiceMock;
#InjectMocks
private FormController formController;
#Before
public void setup() {
// Process mock annotations
MockitoAnnotations.initMocks(this);
// Setup Spring test in standalone mode
this.mockMvc = MockMvcBuilders.standaloneSetup(formController).build();
}
#Test
public void testGet() throws Exception {
when(formServiceMock.getFormImplById(1)).thenReturn(formBean);
mockMvc.perform(get("/Form/form/{id}", 1))
.andExpect(status().is2xxSuccessful())
.andExpect(content().contentType(MediaType.APPLICATION_JSON));
verify(formServiceMock, times(1)).getFormImplById(1);
verifyZeroInteractions(formServiceMock);
}
}
And here is my controller method:
#RequestMapping(value = "/form/{formId}", method = RequestMethod.GET)
#ResponseBody
public FormImplBean getForm(#PathVariable("formId") int formId ) {
return formService.getFormImplById(formId);
}
I keep getting:
java.lang.AssertionError: Content type not set
Of course when I go an look at the real controller on the server, using firefox developer tools, I see that the content type is set correctly.
I tried adding the produces="application/json" to the controller but that did not work, (nor do I think I should have to right?)
Without the content type check, the test passes fine.
I am using:
Spring 4.2.7 -
Mockito 1.10.19 -
Jackson 2.7.0 -
Junit 4.12
in a maven build
Any Ideas?

Null Service while running JUnit test with Spring

I am developing a JUnit test into a Spring Controller, the Controller calls a bunch of Services. My JUnit test code is above:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {WebApplicationConfiguration.class, DatabaseConfiguration.class, ServiceConfiguration.class})
#WebAppConfiguration
public class ProcessFileControllerTest {
private MockMvc mockMvc;
#Test
public void getPageTest() throws Exception{
final ProcessFileController controller = new ProcessFileController();
SecurityHelperUtility.setupSecurityContext("user", "pass", "ROLE_ADMIN");
mockMvc = standaloneSetup(controller).build();
mockMvc.perform(get(URI.create("/processFile.html")).sessionAttr("freeTrialEmailAddress", "")).andExpect(view().name("processFile"));
}
When I run the JUnit test, I get a NullPointerExcelption when I call this particular line of code of my ProcessFileController.java
final Company company = companyService.getCompanyByUser(userName);
I can see that the Serviceis Null.
In my ProcessFileController.java the Service is being declared as:
#Autowired
private CompanyService companyService;
Any clue? Thanks in advance.
You are creating your controller using the new keyword. It should be a spring managed bean for spring to inject its dependencies. Use #Autowired
#Autowired
ProcessFileController controller;

Resources