Spring Boot MockMVC Unit Testing | application/xml;charset=ISO-8859-1 - unit-testing

I'm testing an endpoint and the response content-type is "application/xml;charset=ISO-8859-1", when I expect it to be "application/xml". Can you see where I may have misconfigured the produces aspect? I added it to the #RequestMapping for the function and received the same, unexpected, result.
Feature Under Test
#Controller
#RequestMapping(value = "/sitemaps",
consumes = MediaType.ALL_VALUE,
produces = MediaType.APPLICATION_XML_VALUE)
public class SitemapQueryControllerImpl implements SitemapQueryController {
#RequestMapping(value = "/index.xml", method = RequestMethod.GET)
public ResponseEntity<String> GetSitemapIndex() {
return new ResponseEntity<>("<Hello>", HttpStatus.OK);
}
}
Test
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = SitemapQueryControllerImpl.class, secure = false)
#ContextConfiguration(classes = {ApplicationTestContext.class})
public class SitemapQueryController_Spec {
#Autowired
private MockMvc mockMvc;
#Before
public void setup() { }
#Test
public void GetSitemapIndex_Successul() throws Exception {
String expect = "<Hello>";
mockMvc.perform(get("/sitemaps/index.xml")
.contentType(MediaType.APPLICATION_XML_VALUE))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_XML_VALUE))
.andExpect(content().xml(expect));
}

By default charset is UTF-8, MappingJackson2HttpMessageConverter is who manages the charSet. You can override by implementing the bean and setting charSet to null.
#Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
jsonConverter.setObjectMapper(objectMapper);
jsonConverter.setDefaultCharset(null);
return jsonConverter;
}

If you just use application/xml as your produces/accepts configuration (as is done through the method you use), it uses the default charset, which for compatibility reasons is set to the ISO charset you're getting returned. I've encountered the same issue today and yesterday, and the solution is to explicitly pass application/xml;charset=utf-8 as contentType and accept headers, and check that you get application/xml;charset=utf-8 as the contentType of the returned content. The easiest way to do this is to construct it using the new MediaType(MediaType.APPLICATION_XML.getType(), MediaType.APPLICATION_XML.getSubType(), StandardCharSets.UTF_8) constructor to create a new MediaType for application/xml;charset=UTF-8 that you can then use in your test requests.

Related

How can I use MockWebServer for Junit test cases of webclient?

I have a spring-boot application which calls some third party URL (let's say http://example.com/someUri) using webclient(I have used application-dev.properties for injecting this url in my application to achieve loose coupling) and consumes the response and use it in my application.
It's my first time when I am going to write test cases for webclient. and there I used #SprintBootTest.
I found that there are two ways where I can test my webclient with third party Api call by mocking the api call and make it call to my local url(which will be using url(http://localhost:{portNumber}/someUri) from my testing properties file: src/test/resources/application.properties) where It will be giving some mockedResponse in return to my real client:
Using wiremock
Using MockWebServer
consider above code for better understanding:
#Service
Class SampleService{
#Value("${sample.url}")
private String sampleUrl;
public String dummyClient() {
String sample =webClient.get()
.uri(sampleUrl)
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.retrieve()
.bodyToMono(String.class)
.block();
return sample;
}
}
application-dev.properties:
sample.url:http://example.com/someUri
src/test/resouces/application.properties:
http://localhost:8090/someUri
Testing class:
#SpringBootTest
public class sampleTestingClass {
#Autowired
private SampleService sampleService;
#Value("${sample.url}")
private String sampleUrl;
public static MockWebServer mockWebServer = new MockWebServer();
#BeforeAll
static void setUp() throws IOException {
mockWebServer.start(8090);
}
#AfterAll
static void tearUp() throws IOException {
mockWebServer.close();
}
HttpUrl url = mockWebServer.url("/someUri");
mockWebServer
.enqueue(
new MockResponse()
.setResponseCode(200)
.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.setBody("Sample Successful"));
String sample = sampleService.dummyClient();
assertEquals(sample ,matches("Sample Successful"));
}
}
but this code isn't working. it's giving me above error:
java.lang.NullPointerException
It will be really helpful if anybody knows how this can be fixed to achieve my unit testing using mocked Url? Thanks in advance!
Here is a working example:
#Component
public class QuotesClient {
private final WebClient webClient;
public QuotesClient(WebClient.Builder builder, #Value("${client.baseUrl}") String baseUrl) {
this.webClient = builder.baseUrl(baseUrl).build();
}
public JsonNode getData() {
return this.webClient
.get()
.retrieve()
.bodyToMono(JsonNode.class)
.block();
}
}
Using the WebClient.Builder is optional.
The corresponding test can look like the following:
class QuotesClientTest {
private QuotesClient quotesClient;
private MockWebServer server;
#BeforeEach
public void setup() {
this.server = new MockWebServer();
this.quotesClient = new QuotesClient(WebClient.builder(), server.url("/").toString());
}
#Test
public void test() {
server.enqueue(new MockResponse()
.setStatus("HTTP/1.1 200")
.setBody("{\"bar\":\"barbar\",\"foo\":\"foofoo\"}")
.addHeader("Content-Type", "application/json"));
JsonNode data = quotesClient.getData();
assertNotNull(data);
System.out.println(data);
}
}
If you are searching for a similar setup using WireMock, Spring Boot, and JUnit 5, take a look at the linked guide.

***HELP*** Test/Mock: Service Class using Rest Template, Response and HTTP Entities, Exceptions

I have a service class, with for readability purpose, I have provided the code with dummy variables and objects. I am trying to write a JUNIT test class for the service, primarily with Mockito. No matter how hard I try, I am not able to hit the method serviceMethod irrespective of using spy/mock. I have also included a test, following the main class.
I know I am missing something here, but doesn't cross my mind. I need an eye to review this and let me know how I can write a proper test class for this and obtain coverage for the method.
(P.S. all the necessary imports are in-place and not pasted here to keep this concise)
Thanks in advance!
#Service
public class ServiceClass {
private static final Logger LOGGER = LoggerFactory.getLogger(ServiceClass.class);
#Autowired
String stringUrl;
RestTemplate restTemplate = new RestTemplate();
public void serviceMethod(ModelObject model) {
try {
HttpEntity<ModelObject> request = new HttpEntity<>(model);
ResponseEntity<String> response = restTemplate.exchange(stringUrl,
HttpMethod.POST, request, String.class);
LOGGER.info(response.getBody() + "and " + response.getStatusCode());
} catch (HttpClientErrorException exception) {
LOGGER.info(exception.getResponseBodyAsString());
} catch (HttpStatusCodeException exception) {
LOGGER.info(exception.getResponseBodyAsString());
}
}
Sample Test:
#RunWith(MockitoJUnitRunner.Silent.class)
public class ServiceClassTest {
#InjectMocks
private ServiceClass serviceClass;
#Mock
private RestTemplate restTemplate;
#Test
public void testServiceMethod() {
ServiceClass spy = Mockito.spy(serviceClass);
// ServiceClass spy = mock(ServiceClass.class, Mockito.CALLS_REAL_METHODS);
doNothing().when(spy).serviceMethod(Mockito.any(ModelObject.class));
Mockito.doCallRealMethod().when(spy).serviceMethod(Mockito.any(ModelObject.class));
ResponseEntity<String> responseEntity = new ResponseEntity<>(HttpStatus.ACCEPTED);
Mockito.when(restTemplate.exchange(ArgumentMatchers.anyString(), ArgumentMatchers.any(HttpMethod.class),
ArgumentMatchers.<HttpEntity<ModelObject>>any(), ArgumentMatchers.<Class<String>>any()))
.thenReturn(responseEntity);
}

How to test POST method in Spring boot using Mockito and JUnit

I am newbie to Unit testing using JUnit and Mockito in Spring boot framework.
I want to test this method. How to test POST Request method:
// add Employee
#RequestMapping(method = RequestMethod.POST)
public void addEmployee(#RequestBody Employee employee){
this.employeeService.addEmployee(employee);
}
Thank you in advance
As #merve-sahin rightly pointed out, you can use #WebMvcTest to achieve this.
Look at the following example :
#RunWith(SpringRunner.class)
#WebMvcTest(YourController.class)
public class YourControllerTest {
#Autowired MockMvc mvc;
#MockBean EmployeeService employeeService;
#Test
public void addEmployeeTest() throws Exception {
Employee emp = createEmployee();
mvc.perform(post("/api/employee")
.contentType(MediaType.APPLICATION_JSON)
.content(toJson(emp)))
.andExpect(status().isOk());
}
}
In Above code you can mock your dependent service using #MockBean.
The test will perform post on your custom Employee object and validate the response
You can add headers, authorization while calling perform
Assuming you using JSON as media type, you can write toJson() method using any json library to convert Employee object into Json string format
private String toJson(Employee emp) {
If you are using XML, then you can do the same for XML
You can validate the response using expectations in chained way.
As rightly pointed out, please checkout MockedMvc link which should help you
Go through this following example:
#RunWith(SpringJUnit4ClassRunner.class)
public class ApplicationControllerTest {
#Mock
EmployeeService employeeService;
private MockMvc mockMvc;
#Before
public void setUp() throws Exception {
initMocks(this);
YourController controller = new YourController(employeeService);
mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
}
#Test
public void addEmployee() throws Exception {
Employee emp = new Employee("emp_id","emp_name");//whichever data your entity class have
Mockito.when(employeeService.addEmployee(Mockito.any(Employee.class))).thenReturn(emp);
mockMvc.perform(MockMvcRequestBuilders.post("/employees")
.content(asJsonString(emp))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().contentType("application/json;charset=UTF-8"));
}
public static String asJsonString(final Object obj) {
try {
return new ObjectMapper().writeValueAsString(obj);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
In above given example mock your service class which is required to post the data to your Employee entity class.
I'm assuming that you're doing this via controller so you first need to initialize the controller which comes under the #Before annotation.
By doing above example you'll be able to post your data into the JSON format.
The below example is using JUnit5, Mockito3.x, spring-boot2.4.4, and
assertj3.x
The spring-boot-starter-test dependency from version 2.2.0
already comes with Junit 5 and contains also Hamcrest, assertj, and Mockito libraries.
In JUnit 5, “Runner” extension points, available in JUnit 4, are replaced by the Extension API.
You can register the Mockito extension via #ExtendWith.
Initializes mocks annotated with #Mock annotation so that explicit usage of MockitoAnnotations#initMocks(Object) is not required.
From spring-boot 2.1, there is no need to load the SpringExtension using annotation #ExtendWith because it's included as a meta-annotation in these annotations #DataJpaTest, #WebMvcTest, and #SpringBootTest.
Complete example with Github link: https://github.com/jdamit/DemoSpringBootApp.git
**#WebMvcTest(controllers = UserController.class)**
public class UserControllerTest {
#Autowired
private MockMvc mockMvc;
#Autowired
private ObjectMapper mapper;
#MockBean
private UserServiceImpl userService;
private List<UserDto> users;
private UserDto user;
private String URI = "/users";
#BeforeEach
void setUp(){
users = List.of(new UserDto("Amit", "Kushwaha", "jdamit2027#gmail.com", "sector 120"),
new UserDto("Amit", "Kushwaha", "jdamit2027#gmail.com", "sector 120"),
new UserDto("Amit", "Kushwaha", "jdamit2027#gmail.com", "sector 120"));
user = new UserDto("Rahul", "Swagger", "rahul.swagger#gmail.com", "sector 120");
}
#Test
//#Disabled
void getUsersTest() throws Exception {
Mockito.when(userService.getUsers()).thenReturn(users);
MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get(URI)
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)).andExpect(MockMvcResultMatchers.status().isOk())
.andReturn();
Assertions.assertThat(result).isNotNull();
String userJson = result.getResponse().getContentAsString();
Assertions.assertThat(userJson).isEqualToIgnoringCase(mapper.writeValueAsString(users));
}
#Test
//#Disabled
void createUserTest() throws Exception {
Mockito.when(userService.createUser(Mockito.any(UserDto.class))).thenReturn(user);
MvcResult result = mockMvc.perform(MockMvcRequestBuilders.post(URI)
.contentType(MediaType.APPLICATION_JSON)
.content(mapper.writeValueAsString(user).getBytes(StandardCharsets.UTF_8))
.accept(MediaType.APPLICATION_JSON)).andExpect(MockMvcResultMatchers.status().isOk())
.andReturn();
Assertions.assertThat(result).isNotNull();
String userJson = result.getResponse().getContentAsString();
Assertions.assertThat(userJson).isNotEmpty();
Assertions.assertThat(userJson).isEqualToIgnoringCase(mapper.writeValueAsString(user));
}
}

#WithUserDetails does not seem to work

I have an application in which I use Spring Social Security for authentication and authorization. Unfortunately I am having some problems with mocking Spring Security. It seems that it does not work at all.
I have a REST controller that returns 404 Not Found if the identifier of the entity it should return is not available. If the user is not logged in then any page redirects to the social login page of my app.
I have read here that the #WithUserDetails annotation would suit me the best.
So my test method looks like this
#Test
#SqlGroup({
#Sql(executionPhase = ExecutionPhase.BEFORE_TEST_METHOD, statements = "INSERT INTO UserAccount(id, creationtime, modificationtime, version, email, firstname, lastname, role, signinprovider) VALUES (1, '2008-08-08 20:08:08', '2008-08-08 20:08:08', 1, 'user', 'John', 'Doe', 'ROLE_USER', 'FACEBOOK')"), })
#Rollback
#WithUserDetails
public void ifNoTeamsInTheDatabaseThenTheRestControllerShouldReturnNotFoundHttpStatus() {
ResponseEntity<String> response = restTemplate.getForEntity("/getTeamHistory/{team}", String.class, "Team");
Assert.assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode());
}
But this does not seem to work at all. It looks like the test method is executed with anonymous user, because the status I get is 200 OK.
My test class is annotated like this
#RunWith(SpringRunner.class)
#ActiveProfiles("dev")
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
#Transactional
public class TeamRestControllerTest {
//...
}
Has anyone ever experienced such an issue with mocking Spring Security that is delivered by Spring Social?
I'm unable to test it at the moment, but here's a possible solution.
Looking at #WithUserDetails implementation:
#WithSecurityContext(factory = WithUserDetailsSecurityContextFactory.class)
public #interface WithUserDetails {
...
}
final class WithUserDetailsSecurityContextFactory implements
WithSecurityContextFactory<WithUserDetails> {
private BeanFactory beans;
#Autowired
public WithUserDetailsSecurityContextFactory(BeanFactory beans) {
this.beans = beans;
}
public SecurityContext createSecurityContext(WithUserDetails withUser) {
String beanName = withUser.userDetailsServiceBeanName();
UserDetailsService userDetailsService = StringUtils.hasLength(beanName)
? this.beans.getBean(beanName, UserDetailsService.class)
: this.beans.getBean(UserDetailsService.class);
String username = withUser.value();
Assert.hasLength(username, "value() must be non empty String");
UserDetails principal = userDetailsService.loadUserByUsername(username);
Authentication authentication = new UsernamePasswordAuthenticationToken(
principal, principal.getPassword(), principal.getAuthorities());
SecurityContext context = SecurityContextHolder.createEmptyContext();
context.setAuthentication(authentication);
return context;
}
}
You could create the Security Context of your choice following the same pattern:
#Target({ElementType.METHOD, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Inherited
#Documented
#WithSecurityContext(factory = WithoutUserFactory.class)
public #interface WithoutUser {
}
public class WithoutUserFactory implements WithSecurityContextFactory<WithoutUser> {
public SecurityContext createSecurityContext(WithoutUser withoutUser) {
return SecurityContextHolder.createEmptyContext();
}
}
The other available annotations: WithAnonymousUser, WithMockUser, WithSecurityContext (and WithUserDetails)
Adding my workaround, probably it can be helpful for someone else.
I think I met the same problem:
A #Testcontainers (for PostgreSQL DB emulation) + #SpringBootTest test.
Mocked the SecurityContext via annotation with #WithSecurityContext with mocking factory.
I need this mocking for an Envers RevisionListener, where I get the userName and userId from the SecurityContext created normally by Keycloak.
When calling the Spring beans in the test, mocking works ok.
But when calling the API via TestRestTemplate, SecurityContext is not mocked and is returning a null for all fields (principal, etc).
The original class looks like this:
#SpringBootTest(
classes = SpringBootInitializer.class,
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
properties = {"keycloak.enabled=false"}
)
#ContextConfiguration(
classes = PersistenceConfiguration.class,
initializers = MyTest.Initializer.class
)
// !!! the SecurityContext mocking will NOT work when calling the controller via REST
#MockKeycloakUser() // do not fail on getting Keycloak data in UserDataRevisionListener
#EnableAutoConfiguration(exclude = { SecurityAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class }) // turn off Spring Security to avoid 401 and 302 responses
#Testcontainers // required to fill #Container fields with containers
#Log4j2
#ActiveProfiles("integration-test")
class MyTest {
#Autowired
private TestRestTemplate restTemplate;
// ...
// call via restTemplate looks like this
private List<MyDTO> executeSearchQuery(String query) {
String searchUrl = getSearchUrl(port, query, filter);
MyDTO[] results = this.restTemplate.getForObject(searchUrl, MyDTO[].class);
return List.of(results);
}
// ...
}
What I used to make the SecurityContext work is:
Add the MockMvc field to the test class.
Add #AutoConfigureMockMvc on the test class.
!!! Execute the API via MockMvc instead of TestRestTemplate
Looks like this:
// all other annotations on the test class stay the same
#AutoConfigureMockMvc // make MockMvc work
// ...
class MyTest {
#Autowired
private MockMvc mockMvc; // trick to make the mock SecurityContext work, which does not work when calling via TestRestTemplate
// Execute the API via mockMvc looks like this:
private String getApiResponse(MyRequest request, int expectedHttpStatus) {
final String url = getRequestUrl();
final String requestBody = JacksonUtils.serializeToString(request);
try {
final MockHttpServletRequestBuilder builder = MockMvcRequestBuilders
.post(url)
.contentType(MediaType.APPLICATION_JSON)
.content(requestBody)
;
// use MockMvc instead of TestRestTemplate to successfully use the mock user emulation
return mockMvc
.perform(builder)
.andExpect(status().is(expectedHttpStatus))
.andReturn()
.getResponse()
.getContentAsString(StandardCharsets.UTF_8);
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
// ...
}

Mock object is not injected

I try mock controler:
#RestController
public class SthControl {
#Autowired
private ObjRepo repo;
#RequestMapping(value = "/dosth", method = RequestMethod.POST, produces = "application/json")
public ModelMap handleSth(#RequestParam("key") String key) {
final Logger logger = Logger.getLogger(getClass());
logger.info("Is Mock "+ new MockUtil().isMock(repo));//return FALSE- is real object
logger.info("Key " + repo.loadByKey(key);//return NULL- always call real Method
Test Case:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("file:src/main/webapp/WEB-INF/mvc-dispatcher-servlet.xml")
#WebAppConfiguration
public class SthControlTest {
#SuppressWarnings("SpringJavaAutowiringInspection")
#Autowired
protected WebApplicationContext wac;
private MockMvc mockMvc;
#Mock
private ObjRepo repo;
#InjectMocks
#Autowired
private SthControl contr;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
this.mockMvc = webAppContextSetup(this.wac).build();
BasicConfigurator.configure();
}
#Test
public void testRegister() throws Exception {
final UUID uuid = UUID.randomUUID();
final String keyValue = "KeyVal";
final Logger logger = Logger.getLogger(getClass());
repo = Mockito.mock(ObjtRepo.class);
Mockito.when(repo.loadByKey(keyValue)).thenReturn(new Obj(uuid, keyValue, TimeUtils.currentTimeSecond(), false));
Problem still exist if replace #Mock Annotation with this lines
repo = Mockito.mock(ObjRepo.class);
ReflectionTestUtils.setField(contr, "repo", repo, ObjRepo.class);
logger.info("Obj " + repo.loadByKey(keyValue).getId());//return correct object
logger.info("Mock Is "+new MockUtil().isMock(ReflectionTestUtils.getField(contr,"repo")));//True
There are two issues here.
You need to swap the lines this.mockMvc = webAppContextSetup(this.wac).build(); and MockitoAnnotations.initMocks(this);, otherwise the injection done by the Spring web application context setup will overwrite the injection done by Mockito. Always do the Spring injection first.
You need to remove the line repo = Mockito.mock(ObjtRepo.class); from testRegister, because this line replaces the value in repo with one that differs from the one you injected, so when you stub the new value, it won't affect the behaviour of SthControl.