Why do I keep getting 404's when using #AutoConfigureMockMvc in SpringBoot test? - spring-boot

I'm trying to write an integration test using springrunner. I'm running it as a springboottest and using autoconfiguremockmvc. However, i keep getting 404s. It seems my controllers/endpoints aren't being loaded by the autoconfiguremockmvc. Does anyone know a solution of how to wire this up so that it will pick up my controllers?
I did add a basic controller into my test class and i was able to hit it successfully but I have been unable so far to hit the actual controller I want to use in my integration test.
#RunWith(SpringRunner.class)
#SpringBootTest(classes = { TestConfig.class })
#TestPropertySource(locations = "classpath:application-test.properties")
#AutoConfigureMockMvc
public class ControllerTest {
#Autowired
private MockMvc mockMvc;
#Test
public void test() throws Exception {
MockHttpServletRequestBuilder request =
MockMvcRequestBuilders.post(URL).contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)
.accept(MediaType.APPLICATION_JSON_UTF8_VALUE)
.content(json);
final ResponseEntity<String> response =
new ResponseEntity<>("works", HttpStatus.OK);
final ResultActions result = this.mockMvc.perform(request).andDo(print());
result.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE));
}
}
#TestConfiguration
#EnableAutoConfiguration
public class TestConfig {
}

You could use
#SpringBootTest(webEnvironment =
SpringBootTest.WebEnvironment.RANDOM_PORT)
and RestAssured library in combination to test any endpoint/api like below
// include dependency in pom
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<version>3.0.5</version>
<scope>test</scope>
</dependency>
//Test Class
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class IntTest{
//inject test local port
#LocalServerPort
private int port;
#Before
public void setUp() {
//assign port for reset assured
RestAssured.port = port;
}
#Test
public void test() {
given()
.get("/api")
.then()
.assertThat()
.statusCode(HttpStatus.OK.value())
.contentType(ContentType.JSON)
.body("name", is("test"));
}
}

Related

#MockBean with Junit Jupiter concurrent mode

I am trying to use
junit.jupiter.execution.parallel.mode.default=concurrent
along with #MockBean from Spring Boot. However, the tests starts to fail when I set to concurrent mode. I tried setting
#MockBean(reset = MockReset.NONE)
However, it also does not help. Seems like mocked bean is reinitialized / reset even though MockReset.NONE is set.
Is that possible to use #MockBean allow with concurrent mode or is it a known limitation?
Bean I am mocking:
#Service
public class SampleService {
public Mono<String> processCall(String call) {
return Mono.just("ok");
}
}
The controller I am testing:
#RestController
#RequiredArgsConstructor
public class SampleController {
private final SampleService sampleService;
#PostMapping("/call")
public Mono<String> processCall(#RequestBody String body) {
return sampleService.processCall(body);
}
}
And tests:
#ExtendWith(SpringExtension.class)
#WebFluxTest(SampleController.class)
#ContextConfiguration(classes = {SampleController.class})
class SampleTest {
#MockBean(reset = MockReset.NONE)
private SampleService sampleService;
#Autowired
private WebTestClient webTestClient;
#Test
void givenSampleServiceWorksFineExpectOkResponse() {
String randomBody = "1234";
when(sampleService.processCall(randomBody)).thenReturn(Mono.just("ok"));
callService(randomBody).expectStatus().isOk().expectBody(String.class).isEqualTo("ok");
}
#Test
void givenSampleServiceFailedExpectErrorResponse() {
String randomBody = "9876";
when(sampleService.processCall(randomBody))
.thenReturn(Mono.error(new RuntimeException("error")));
callService(randomBody).expectStatus().is5xxServerError();
}
private ResponseSpec callService(String body) {
return webTestClient
.post()
.uri(
uriBuilder ->
uriBuilder
.path("/call")
.build())
.bodyValue(body)
.exchange();
}
}
I set concurrent mode for Jupiter in file junit-platform.properties:
junit.jupiter.execution.parallel.enabled=true
junit.jupiter.execution.parallel.mode.default=concurrent
junit.jupiter.execution.parallel.config.strategy=dynamic

the #Mock and #InjectMocks annotations don't work

rest
#RestController
public class StackOverFlowController {
private StackOverFlowService service;
#Autowired
public StackOverFlowController(
#Qualifier(value = "stackOverFlowServiceImpl")
StackOverFlowService service) {
this.service = service;
}
#RequestMapping("api/stackoverflow")
public List<StackOverFlowDto> getListOfProviders() throws URISyntaxException {
List<StackOverFlowDto> allSites = service.getAllSites();
return allSites;
}
}
service
public interface StackOverFlowService {
List<StackOverFlowDto> getAllSites() throws URISyntaxException;
List<StackOverFlowDto> getAllSitesByTitle(String title) throws URISyntaxException;
}
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${version.mapstruct}</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>29.0-jre</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
test
#RunWith(MockitoJUnitRunner.class)
public class StackOverFlowControllerTest {
#Mock
private StackOverFlowService service ;
#InjectMocks
private StackOverFlowController controller;
#Test
public void getListOfProviders() throws URISyntaxException {
List<StackOverFlowDto> allSites = service.getAllSites();
when(service.getAllSites()).thenReturn(ImmutableList.of());
List<StackOverFlowDto> listOfProviders = controller.getListOfProviders();
verify(service).getAllSites();
}
}
I can create an mock-object this way: :
private StackOverFlowService service = Mockito.mock(StackOverFlowService.class);
update 1
update 2
Wnen I have done this:
#RunWith(SpringRunner.class)
#WebMvcTest(StackOverFlowController.class)
public class StackOverFlowControllerTest {
#Mock
private StackOverFlowService service;
#InjectMocks
StackOverFlowController controller;
#Test
public void getListOfProviders() throws URISyntaxException {
List<StackOverFlowDto> allSites = service.getAllSites();
when(service.getAllSites()).thenReturn(ImmutableList.of());
List<StackOverFlowDto> listOfProviders = controller.getListOfProviders();
verify(service).getAllSites();
}
}
I got an error:
PM org.junit.vintage.engine.descriptor.RunnerTestDescriptor warnAboutUnfilterableRunner WARNING: Runner
org.junit.internal.runners.ErrorReportingRunner (used on class
com.spring.mongo.web.contollers.stackoverflow.StackOverFlowControllerTest)
does not support filtering and will therefore be run completely.
org.junit.runners.model.InvalidTestClassError: Invalid test class
'com.spring.mongo.web.contollers.stackoverflow.StackOverFlowControllerTest':
No runnable methods
where is the method get() from ?
update 3
This the test was performed:
#WebMvcTest(StackOverFlowController.class)
public class StackOverFlowControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private StackOverFlowService service;
#MockBean
private StackOverFlowController controller;
#Test
public void getListOfProviders() throws Exception {
when(service.getAllSites()).thenReturn(ImmutableList.of());
List<StackOverFlowDto> listOfProviders = controller.getListOfProviders();
assertNotNull(listOfProviders);
}
}
But I don't see the point. I don't get any data:
List<StackOverFlowDto> listOfProviders = controller.getListOfProviders();
But here I should get a collection of elements, but I get 0
Update 4
It is my the understanding:
when(service.getAllSites()).thenReturn(ImmutableList.of());
We test a controller. We don't need below layers. We need to validate that the method
getListOfProviders()
of a contoller is called.
So,
service.getAllSites()
here we point Mockito-object
#MockBean
private StackOverFlowService service;
This method should return some sort of collection.
thenReturn(ImmutableList.of())
it should return empty collection.
Why ?
I don't understand this.
List<StackOverFlowDto> listOfProviders = controller.getListOfProviders();
What is it for?
Update 5
#Test
public void getListOfProviders() throws Exception {
when(service.getAllSites()).thenReturn(ImmutableList.of());
List<StackOverFlowDto> listOfProviders = controller.getListOfProviders();
assertNotNull(listOfProviders);
verify(service).getAllSites();
}
verify(service).getAllSites();
This should validate that the method getAllSites() from interface StackOverFlowService (from its implemetation), will call...
But Again I got errors:
Wanted but not invoked:
com.spring.mongo.service.read.stackoverflow.StackOverFlowService#0
bean.getAllSites();
-> at com.spring.mongo.web.contollers.stackoverflow.StackOverFlowControllerTest.getListOfProviders(StackOverFlowControllerTest.java:38)
Actually, there were zero interactions with this mock.
Wanted but not invoked:
com.spring.mongo.service.read.stackoverflow.StackOverFlowService#0
bean.getAllSites();
-> at com.spring.mongo.web.contollers.stackoverflow.StackOverFlowControllerTest.getListOfProviders(StackOverFlowControllerTest.java:38)
Actually, there were zero interactions with this mock.
Why ?
Update 6
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {SpringMongoApplication.class})
#WebMvcTest(StackOverFlowController.class)
class StackOverFlowControllerTestUnit {
#Autowired
private MockMvc mockMvc;
#MockBean
private StackOverFlowService service;
#MockBean
private StackOverFlowController controller;
#Test
void getListOfProviders() throws Exception {
MockHttpServletRequestBuilder requestBuilder =
MockMvcRequestBuilders
.get("/api/stackoverflow")
.accept(MediaType.APPLICATION_JSON);
mockMvc.perform(requestBuilder)
.andDo(print())
.andExpect(status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("$.id").exists())
.andDo(print());
}
}
Here beans is created, but i don't result into json...
.andExpect(MockMvcResultMatchers.jsonPath("$.sites").exists())
.andExpect(MockMvcResultMatchers.jsonPath("$.sites[*].id").isNotEmpty());
Update_7
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {SpringMongoApplication.class})
#WebMvcTest(StackOverFlowController.class)
public class StackOverFlowControllerTestFromForum {
#Autowired
private MockMvc mockMvc;
#MockBean
private StackOverFlowService service;
#InjectMocks
private StackOverFlowController controller;
#Test
public void getListOfProviders() throws Exception {
MockHttpServletRequestBuilder requestBuilder =
MockMvcRequestBuilders
.get("/api/stackoverflow")
.accept(MediaType.APPLICATION_JSON);
mockMvc.perform(requestBuilder)
.andDo(print())
.andExpect(status().isOk());
}
}
In this version of the code, all beans are initialized without errors.
#InjectMocks - It is work also.
Normal integration testing offered by Spring simply creates objects
as if the application were running in production. I still don't
understand what Mock-objects are for.
Here any ideas is finished.
I don't understand why it dosen't work.
Сan someone explain the idea of #Mock and #InjectMocks annotations working in relation to this example ?
Does anyone have any ideas?
If you want the controller test to be the same like any other unit test case class then use spring for running tests using annotation #RunWith(SpringRunner.class) .Since you are writing the unit test case for the controller , use the test method like below.You just need to mock the service call and call the controller method or more appropriately hit it via mock mvc :
#Test
public void getListOfProviders() throws URISyntaxException {
when(service.getAllSites()).thenReturn(ImmutableList.of());
List<StackOverFlowDto> listOfProviders = controller.getListOfProviders();
assertNotNull(listOfProviders);
}
But if you want it to be the right way then :
#WebMvcTest
public class StackOverFlowControllerTest {
#Mock
private StackOverFlowService service ;
#Autowired
private MockMvc mockMvc;
#Test
public void getListOfProviders() throws URISyntaxException {
when(service.getAllSites()).thenReturn(ImmutableList.of());
this.mockMvc.perform(get("api/stackoverflow")).andDo(print()).andExpect(status().isOk());
}
}
If you are having issues ith #WebMvcTest it means springboot is having trouble setting up the test env because of project structure or annotation on main application class that might be causing issues for test setup so in that case try setting up an entire application context like in an integration test (a bit tedious and unnecessary ) by using the #SpringBootTest annotation instead.
Official doc link.
Try with #RunWith(SpringRunner.class) instead of #RunWith(MockitoJUnitRunner.class), it should work. SpringRunner will help with Dependency Injection of the mocks.
Edit:
Try with the code below:
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {SpringMongoApplication.class})
#WebMvcTest(StackOverFlowController.class)
public class StackOverFlowControllerTestUnit {
#Autowired
private MockMvc mockMvc;
#MockBean
private StackOverFlowService service;
#Test
void getListOfProviders() throws Exception {
MockHttpServletRequestBuilder requestBuilder =
MockMvcRequestBuilders
.get("/api/stackoverflow")
.accept(MediaType.APPLICATION_JSON);
mockMvc.perform(requestBuilder)
.andDo(print())
.andExpect(status().isOk());
// .andExpect(MockMvcResultMatchers.jsonPath("$.id").exists())
// .andDo(print());
}
}
Try going step by step. If the above works then maybe you can go for the json path verification

Integration tests on spring-boot throws Connection refused

I have an unit test on Spring Boot:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = Application.class)
public class CustomerControllerIT {
private RestTemplate restTemplate = new RestTemplate();
#Test
public void findAllCustomers() throws Exception {
ResponseEntity<List<Customer>> responseEntity = restTemplate.exchange(
"http://localhost:8080/Customer", HttpMethod.GET, null,
new ParameterizedTypeReference<List<Customer>>() {
});
List<Customer> list = responseEntity.getBody();
Assert.assertEquals(list.size(), 0);
}
}
If I launch test on started application - tests ok
If I try to launch only IT, there is connection refused error
My application.properties is same for single start.
For tests and located in resources and testResources.
Application.class is:
#ComponentScan({"mypackage"})
#EntityScan(basePackages = {"mypackage.model"})
#EnableJpaRepositories(basePackages = {"mypackage.persistence"})
#SpringBootApplication
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
You must run a test with a running server ,
If you need to start a full running server, you can use random ports:
#SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT)
An available port is picked at random each time your test runs
You need this maven dependency:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
</dependency>
Example:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT)
public class TestRest {
#Autowired
private TestRestTemplate restTemplate;
#Test
public void findAllCustomers() throws Exception {
ResponseEntity<List<Customer>> responseEntity = restTemplate.exchange(
"/Customer", HttpMethod.GET, null,
new ParameterizedTypeReference<List<Customer>>(){});
List<Customer> list = responseEntity.getBody();
Assert.assertEquals(list.size(), 0);
}
}
Please read the documentation for more reference
For running #SpringBootTest and JUnit5 (Jupiter) with static port you can use:
#ExtendWith(SpringExtension.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
class MyUnitTests {
#Test
void checkActuator() throws Exception {
final String url = "http://localhost:8080/actuator/health";
#SuppressWarnings("rawtypes")
ResponseEntity<Map> re = new RestTemplate().getForEntity(url, Map.class);
System.out.println(re);
assertEquals(HttpStatus.OK, re.getStatusCode());
re.getStatusCode();
}
}
If you're using JUnit 4:
Change: #ExtendWith(SpringExtension.class)
To: #RunWith(SpringRunner.class)
And then add the port property to your application.yml (inside folder src/main/resources):
server:
port: 8080
Or if have an application.properties:
server.port=8080
Reference:
How to configure port for a Spring Boot application
https://www.baeldung.com/spring-boot-change-port
Test the SpringBoot application startup

JUnit not working in Spring Boot with #Autowired annotation

I have a Spring Boot application in which I'm creating REST web services
using the MVC pattern.
I have a controller, service and DAO class and I use the #Autowired annotation for calling methods of the service and DAO layer.
When I create JUnit tests using mockito, the values are going into the controller but from the controller they are not going to the service class.
Here is the code sample:
#WebAppConfiguration
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {AppplicationConfiguration.class})
public class ExternalControllerTest {
private MockMvc mockMvc;
#InjectMocks
private MyController myController;
#MockBean
myService myService;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders
.standaloneSetup(myController)
.build();
}
#Test
public void testListCluster() throws Exception {
Input emrInput = new Input();
emrInput.setId("200004773");
emrInput.setName("test");
String expected = "{\"status\":\"Success\",\"message\":\"Success\",\"data\":\"somevalue\"}";
AutomateRestResponse response = new AutomateRestResponse<JsonObject>();
response.setMessage("Success");
response.setStatus("Success");
response.setData("somevalue");
Mockito.when(
externalService.listCluster(emrInput)
).thenReturn(response);
mockMvc.perform(post("/v1/gerData"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.status", is("Success")));
verify(externalService, times(1)).listCluster(emrInput);
RequestBuilder requestBuilder = MockMvcRequestBuilders
.post("/v4/listCluster")
.accept(MediaType.APPLICATION_JSON).content(emrInputJosn)
.contentType(MediaType.APPLICATION_JSON);
MvcResult result = mockMvc.perform(requestBuilder).andReturn();
System.out.println("response body1" + result.getResponse()
.getContentAsString());
}`
Please help me with this.
It is not clear from your question what you are trying to mock.
Anyway, you should not be able to debug your service/dao that is mocked since what actually executed in the test is the mocked one and not yours.
If you want to test your controller, you can mock your service or dao and define what the response they will return, and then verify that the response you get from your controller is as you expect it to be.
#EnableWebMvc
#SpringBootApplication(scanBasePackages = { "com.yourPackage.external" })
public class YourApplication extends org.springframework.boot.web.support.SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(applicationClass, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(applicationClass);
}
private static Class<Application> applicationClass = Application.class;
}
Based on what you have pasted, you can do below things:
if you are using #RunWith(SpringJUnit4ClassRunner.class) [better change to #RunWith(SpringRunner.class)] then use
#MockBean
private MyService externalService;
OR
use #RunWith(MockitoJUnitRunner.class) and
#MockBean
private MyService externalService;
#InjectMocks
private MyController controller = new MyController(externalService);
for details checkout :- testing web in spring boot
#WebAppConfiguration
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {AppplicationConfiguration.class})
public class ExternalControllerTest {
private MockMvc mockMvc;
#InjectMocks
private MyController myController ;
#MockBean
myService myService;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders
.standaloneSetup(myController)
.build();
}
#Test
public void testListCluster() throws Exception {
Input emrInput = new Input();
emrInput.setId("200004773");
emrInput.setName("test");
String expected = "{\"status\":\"Success\",\"message\":\"Success\",\"data\":\"somevalue\"}";
AutomateRestResponse response = new AutomateRestResponse<JsonObject>();
response.setMessage("Success");
response.setStatus("Success");
response.setData("somevalue");
Mockito.when(
externalService.listCluster(emrInput)
).thenReturn(response);
mockMvc.perform(post("/v1/gerData"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.status", is("Success")));
verify(externalService, times(1)).listCluster(emrInput);
RequestBuilder requestBuilder = MockMvcRequestBuilders
.post("/v4/listCluster")
.accept(MediaType.APPLICATION_JSON).content(emrInputJosn)
.contentType(MediaType.APPLICATION_JSON);
MvcResult result = mockMvc.perform(requestBuilder).andReturn();
System.out.println("response body1"+ result.getResponse()
.getContentAsString());
}
}

How do I change this Spring Test Framework Test to work with a JSON return?

I am trying to change the following Spring Test Framework to test for a the following json return:
{"user":"John ","name":"JohnSmith"}
Here is my test code and I just dont know how to change it to check for JSON and the value in JSON. It will be great if someone can tell me what to change.. thanks
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {WebMVCConfig.class})
#WebAppConfiguration
public class TestHelloWorldWeb
{
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Before
public void setup()
{
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
#Test
public void getFoo() throws Exception
{
this.mockMvc.perform(get("/ask")
.accept(MediaType.TEXT_HTML))
.andExpect(status().isOk())
.andExpect(view().name("helloworld"))
.andExpect(MockMvcResultMatchers.model().attribute("user", "JohnSmith"))
;
}
}
.andExpect(jsonPath("$.user").value("john"));
with dependency :
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>0.8.1</version>
</dependency>
and one static import like this
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
adds the static method...

Resources