When I try using the below code to test the controllers no errors happen, it just says terminated with no logged messages or anything.
import static org.junit.Assert.assertEquals;
import java.util.Arrays;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.RequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import ....controllers.UserController;
import ....data.response.UserResponse;
import ....models.user.User;
#RunWith(SpringRunner.class)
#WebMvcTest(UserController.class)
public class UserWebMvc {
#Autowired
private MockMvc mvc;
#Test
public void givenEmployees_whenGetEmployees_thenReturnJsonArray() throws Exception {
User alex = new User();
List<UserResponse> allUsers = Arrays.asList(new UserResponse(alex.getId(), alex.getInfo()));
RequestBuilder request = MockMvcRequestBuilders.get("/hello");
MvcResult result = mvc.perform(request).andReturn();
assertEquals(result.getResponse().getContentAsString(), "hello");
// userService.createUser(
// new UserRequest(alex.getInfo().getName(), alex.getInfo().getEmail(), alex.getInfo().getPassword()));
//
}
}
However my test to check that junit is working runs fine, so I'm thinking its something to do with SpringRunner or WebMvc
#SpringBootTest
class BackendApplicationTests {
#Test
void contextLoads() {
assertTrue(false);
}
}
Isn't assertTrue(false) always going to be false? You are basically checking whether false equals true, which will always fail.
Also, from the Spring documentation regarding #WebMvcTest:
Using this annotation will disable full auto-configuration and instead apply only configuration relevant to MVC tests (i.e. #Controller, #ControllerAdvice, #JsonComponent, Converter/GenericConverter, Filter, WebMvcConfigurer and HandlerMethodArgumentResolver beans but not #Component, #Service or #Repository beans).
Using this annotation, it will not scan any beans from the service layer. So if you have a UserService which is injected into the UserController, it will not be found by Spring.
When I run tests like this, I would do something like:
class UserControllerTest {
#InjectMocks
private UserController userController;
private MockMvc mockMvc;
#BeforeEach
public void setUp() {
MockitoAnnotations.openMocks(this);
mockMvc =
MockMvcBuilders.standaloneSetup(userController).build();
}
#Test
public void givenEmployees_whenGetEmployees_thenReturnJsonArray()
throws Exception {
User alex = new User();
List<UserResponse> allUsers = Arrays.asList(new
UserResponse(alex.getId(), alex.getInfo()));
RequestBuilder request = MockMvcRequestBuilders.get("/hello");
MvcResult result = mvc.perform(request).andReturn();
assertEquals(result.getResponse().getContentAsString(),
"hello");
}
}
I was using the wrong import. Changing to this fixed it
import static org.junit.Assert.assertTrue;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
Related
I am new to spring boot and Junit. I am writing a small Junit testcase for my controller.
My controller file looks like below:
#RequestMapping(path = "/project-files")
public class MyController {
private RequestModel setRequestModel(String params) {
RequestModel requestModel = new RequestModel();
requestModel.setRequestData(params);
return requestModel;
}
#PostMapping("/deleteUploadedFile")
#ResponseBody
public Map<String, Object> deleteUploadedFile(#RequestParam(value = "requestData", required = false) String params) {
RequestModel requestModel = setRequestModel(params);
return service.deleteUploadedFile(requestModel);
}
}
Based on the reference from here I tried to write a junit tescases for my controller.
My Junit Testcases looks like below:
package com.test.project.web;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.anyMap;
import static org.mockito.Mockito.when;
import java.io.IOException;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import com.test.project.beans.RequestModel;
import com.test.project.service.serviceImpl;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
#RunWith(SpringRunner.class)
#WebMvcTest(MyController.class)
public class MyControllerTest<mapFromJson> {
#InjectMocks
private serviceImpl serviceImpl;
#InjectMocks
dataRepoImpl repoImpl;
#InjectMocks
MyController myController;
private MockMvc mockMvc;
#Before
public void setup() {
this.mockMvc = MockMvcBuilders.standaloneSetup(new MyController()).build();
}
protected <T> T mapFromJson(String json, Class<T> clazz) throws
JsonParseException, JsonMappingException, IOException {
ObjectMapper objectMapper = new ObjectMapper(); return
objectMapper.readValue(json, clazz);
}
#Test
public void deletefile() throws Exception {
String uri = "/deleteUploadedFile";
MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.get(uri).accept(MediaType.APPLICATION_JSON_VALUE))
.andReturn();
int status = mvcResult.getResponse().getStatus();
assertEquals(200, status);
String content = mvcResult.getResponse().getContentAsString();
MyController[] productlist = super.mapFromJson(content, MyController[].class);
assertTrue(productlist.length > 0);
}
}
I am getting errors
java.lang.IllegalStateException: Failed to load ApplicationContext
Is there any other better way to write the Junit testcases for controller?
#Test public void deletefile() throws Exception {
MvcResult mvcResult = mockMvc.perform(post("/project-files/deleteUploadedFile") //Getting error in post.
.param("params", "some_value_here"))
.andExpect(status().isOk()); //Getting error in status
String content = mvcResult.getResponse().getContentAsString();
productlist = super.mapFromJson(content, MrmController[].class); //Getting error in productlist & mapFromJson
assertTrue(productlist.length > 0);
}
You can use the .andExpect() method to validate the status of the request instead of using assertEquals()
You can use something in the lines of this to test your endpoint call:
mockMvc.perform(post("/project-files/deleteUploadedFile")
.param("params", "some_value_here"))
.andExpect(status().isOk());
Some strange things I find inside of your controller is that you use #PostMapping for deleting you can use #DeleteMapping instead see spring docs here
Also, you should use more descriptive names for parameter names (and in general)
String params
don't really ring a bell as to what this variable is representing.
If the issue persists make sure to post the entire stacktrace as suggested.
*Keep in mind when you're writing tests in general you are mimicking the behaviour of the real call, so don't add useless checks or mocks
#InjectMocks
private serviceImpl serviceImpl;
#InjectMocks
dataRepoImpl repoImpl;
I don't see the above in your controller so I am not sure why are they part of the test.
I am writing tests using Spring Cloud Contract.
In my configuration I am using for test an in memory DB that is created and destroyed at each test run.
In order to test successfully I need to put some data in the DB but it seems like the classic #Before annotation used in jUnit does not work (the code in it is never executed).
import io.restassured.module.mockmvc.RestAssuredMockMvc;
import org.junit.Before;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.extension.ExtendWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.web.context.WebApplicationContext;
#ExtendWith(SpringExtension.class)
#SpringBootTest
public class BaseTestClass {
private static final Logger LOGGER = LoggerFactory.getLogger(BaseTestClass.class);
#Autowired
EntityRepository entityRepository;
#Autowired
private WebApplicationContext context;
#Before
public void init() {
// This code never runs
LOGGER.info("Init started");
MyEntity entity1 = new MyEntity();
entityRepository.save(entity1);
}
#BeforeEach
public void setup() {
RestAssuredMockMvc.webAppContextSetup(context);
}
}
What am I missing?
#Before belongs to JUnit4 and earlier while #BeforeEach belongs to JUnit5 as a replacement and clarification of #Before. If you are running with JUnit5, perhaps put all your initialization logic in #BeforeEach (or if it's a one-time init, in #BeforeAll)
I've found a solution that works.
I was adding the #TestInstance decorator incorrectly, here's my working solution:
import io.restassured.module.mockmvc.RestAssuredMockMvc;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.web.context.WebApplicationContext;
#ExtendWith(SpringExtension.class)
#SpringBootTest
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class BaseTestClass {
private static final Logger LOGGER = LoggerFactory.getLogger(BaseTestClass.class);
#Autowired
EntityRepository entityRepository;
#Autowired
private WebApplicationContext context;
#BeforeAll
public void init() {
LOGGER.info("Init started");
MyEntity entity1 = new MyEntity();
entityRepository.save(entity1);
}
#BeforeEach
public void setup() {
RestAssuredMockMvc.webAppContextSetup(context);
}
}
Can you help me? I need to test this controller method. But don't know what to do with httpservletresponse object.
#Controller
public class HomeController {
#PostMapping("/signout")
public String signOut(HttpServletResponse response){
response.addCookie(new Cookie("auth-token", null));
return "redirect:http://localhost:3000";
}
}
Thank you)
Spring MVC Test provides an effective way to test controllers by performing requests and generating responses through the actual DispatcherServlet.
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultMatcher;
#RunWith(SpringRunner.class)
#WebMvcTest(controllers=HomeController.class)
public class HomeControllerTest {
#Autowired
private MockMvc mockMvc;
#Test
public void testSignOut() throws Exception {
mockMvc.perform(post("/signout"))
.andDo(print())
.andExpect(new ResultMatcher() {
#Override
public void match(MvcResult result) throws Exception {
Assert.assertEquals("http://localhost:3000",result.getResponse().getRedirectedUrl());
}
});
}
}
In case of Spring MVC without spring boot, use standalone MockMvc support
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration // or #ContextConfiguration
public class HomeControllerTest{
#Autowired
private HomeController homeController;
private MockMvc mockMvc;
#Before
public void setup() {
// Setup Spring test in standalone mode
this.mockMvc =
MockMvcBuilders.standaloneSetup(homeController).build();
}
We are currently using Spring Boot to connect to a mocked local instance of Amazon SQS. The application itself is working when run, but we would like to try and test the SQS Config class, if possible and if it makes sense.
Here is the configuration class. All properties are pulled from the typical application.properties file when the Spring application itself is run.
import com.amazonaws.services.sqs.AmazonSQSAsync;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.aws.core.env.ResourceIdResolver;
import org.springframework.cloud.aws.messaging.core.QueueMessagingTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class AWSSQSConfig {
#Value("${aws.sqs.endpoint}")
private String AWSSqsEndpoint;
// Producer QueueMessageTemplate
#Bean
public QueueMessagingTemplate queueMessagingTemplate(AmazonSQSAsync amazonSqs, ResourceIdResolver resourceIdResolver) {
if (!AWSSqsEndpoint.isEmpty())
amazonSqs.setEndpoint(AWSSqsEndpoint);
return new QueueMessagingTemplate(amazonSqs, resourceIdResolver);
}
}
Here is the test class. We are attempting to pass the configuration in via TestPropertySource, but they don't actually seem to get to the AWSSQSConfig class. AWSSqsEndpoint inside the instance of the class is always NULL.
import com.amazonaws.services.sqs.AmazonSQSAsync;
import com.lonewolf.formsbuilder.config.AWSSQSConfig;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.aws.core.env.ResourceIdResolver;
import org.springframework.cloud.aws.messaging.core.QueueMessagingTemplate;
import org.springframework.test.context.TestPropertySource;
import static org.junit.Assert.assertNotNull;
#RunWith(MockitoJUnitRunner.class)
#SpringBootTest
#TestPropertySource(properties = {
"cloud.aws.region.static=us-east-1",
"cloud.aws.credentials.accessKey=zzzzz",
"cloud.aws.credentials.secretKey=zzzzzz",
"aws.sqs.endpoint = http://localhost:9324",
"aws.sqs.requestQueue = CreateSchemaRequest",
"aws.sqs.responseQueue = CreateSchemaResponse"
})
public class AWSSQSConfigTests {
#Mock
private AmazonSQSAsync amazonSqs;
#Mock
private ResourceIdResolver resourceIdResolver;
#Test
public void contextLoads() {
AWSSQSConfig config = new AWSSQSConfig();
QueueMessagingTemplate queueMessagingTemplate = config.queueMessagingTemplate(amazonSqs, resourceIdResolver);
assertNotNull("The response body must not be null", queueMessagingTemplate);
}
}
Is this a chicken and the egg situation, where the spring framework actually needs to run first to inject those config values? Do we need an integration test here instead?
EDIT with working solution...
Using the accepted answer, here is my working test! I was able to remove my dependency of the Spring framework.
#RunWith(MockitoJUnitRunner.class)
public class AWSSQSConfigTests {
#Mock
private AmazonSQSAsync amazonSqs;
#Mock
private ResourceIdResolver resourceIdResolver;
#InjectMocks
private AWSSQSConfig config;
#Before
public void setup() {
ReflectionTestUtils.setField(config, "AWSSqsEndpoint", "http://fake");
}
#Test
public void contextLoads() {
QueueMessagingTemplate queueMessagingTemplate = config.queueMessagingTemplate(amazonSqs, resourceIdResolver);
assertNotNull("The response body must not be null", queueMessagingTemplate);
}
}
Have you tried injecting mock to your class (or autowire it), and then setting that field it using ReflectionTestUtils? This is a nice test utils class that Spring provides that allows you to do something like what you want without doing code modifications.
I mean something like this:
#InjectMocks
private AWSSQSConfig awssqsConfig;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
ReflectionTestUtils.setField(awssqsConfig, "AWSSqsEndpoint", "putYourEndpointHere");
}
I'm struggling with testing access control on URLs protected by Spring Security.
The configuration looks like this:
http
.authorizeRequests()
.antMatchers("/api/user/**", "/user").authenticated()
.antMatchers("/api/admin/**", "/templates/admin/**", "/admin/**").hasAuthority("ADMIN")
.anyRequest().permitAll();
And the test class looks like this:
package com.kubukoz.myapp;
import com.kubukoz.myapp.config.WebSecurityConfig;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import javax.transaction.Transactional;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = {MyApplication.class, WebSecurityConfig.class})
#WebAppConfiguration
#TransactionConfiguration(defaultRollback = true)
#Transactional(rollbackOn = Exception.class)
public class MyApplicationTests {
#Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
#Autowired
private FilterChainProxy filterChainProxy;
#Before
public void setUp() {
mockMvc = MockMvcBuilders.webAppContextSetup(context)
.dispatchOptions(true)
.addFilters(filterChainProxy)
.build();
}
#Test
public void testAnonymous() throws Exception {
mockMvc.perform(get("/api/user/account")).andExpect(status().is3xxRedirection());
}
#Test
public void testUserAccessForAccount() throws Exception{
mockMvc.perform(get("/api/user/account")).andExpect(status().isOk());
}
}
What's the easiest way to make the last two tests pass?
#WithMockUser didn't work.
You should not add the FilterChainProxy directly. Instead, you should apply SecurityMockMvcConfigurers.springSecurity() as indicated by the reference. An example is included below:
mockMvc = MockMvcBuilders
.webAppContextSetup(context)
.apply(springSecurity())
.build();
The result of this is:
the FilterChainProxy is added as a Filter to MockMvc (as you did)
the TestSecurityContextHolderPostProcessor is added
Why is TestSecurityContextHolderPostProcessor necessary? The reason is that we need to communicate the current user from the test method to the MockHttpServletRequest that is created. This is necessary because Spring Security's SecurityContextRepositoryFilter will override any value on SecurityContextHolder to be the value found by the current SecurityContextRepository (i.e. the SecurityContext in HttpSession).
Update
Remember anything that contains role in the method name automatically prefixes "ROLE_" to the string that was passed in.
Based on your comment, the problem is you need to either update your configuration to use hasRole instead of hasAuthority (since your annotation is using roles):
.authorizeRequests()
.antMatchers("/api/user/**", "/user").authenticated()
.antMatchers("/api/admin/**", "/templates/admin/**", "/admin/**").hasRole("ADMIN")
.anyRequest().permitAll();
Alternatively
You in Spring Security 4.0.2+ you can use:
#WithMockUser(authorities="ADMIN")
Okay, figured it out.
mockMvc.perform(get("/api/user/account")
.with(user("user")))
.andExpect(status().isOk());
It works now.