Unit testing for POST request using Mockido error - java

The status I expect when testing is 200, but I get 404 instead right now.
I am fairly new to Mockido, so if there is something simple that I am missing. Please let me know.
I have created a POST request in my controller that takes a List of Long objects. If no exception happens, returns OK for status:
#PostMapping(path = "/postlist")
public ResponseEntity<Void> updateAllInList(#RequestBody List<Long> ids) {
try {
// method from ControllerService.java here using ids
return ResponseEntity.status(HttpStatus.OK).body(null);
} catch (InvalidContentException e) {
return ResponseEntity.status(HttpStatus.UNPROCESSABLE_ENTITY).body(null);
}
When I POST using a REST client, I am getting the correct results. The raw payloads I POST are like this:
[
2, 1
]
However, the unit test is giving me a 404.
The way I created the Test class is like this:
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextHierarchy({ #ContextConfiguration(classes = RootConfiguration.class), #ContextConfiguration(classes = WebConfiguration.class) })
#Category(UnitTest.class)
public class ControllerTest {
private static final String POST_REQUEST = "[ 2, 1 ]";
#Autowired private WebApplicationContext webApplicationContext;
#Autowired private ControllerService controllerService;
private MockMvc mockMvc;
#Before
public void setUp() throws Exception {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.webApplicationContext).build();
doNothing().when(this.controllerService).updateAllInList(anyList());
doThrow(InvalidContentException.class).when(this.controllerService).updateAllInList(null);
}
#Test
public void updateList() throws Exception {
this.mockMvc.perform(post("http://testhost/api/configuration/postlist").contentType(MediaType.APPLICATION_JSON_UTF8).content(POST_REQUEST))
.andExpect(status().isOk());
}
#Configuration
static class RootConfiguration {
#Bean
public ControllerService ControllerService() {
return Mockito.mock(ControllerService.class);
}
}
#Configuration
#EnableWebMvc
static class WebConfiguration extends WebMvcConfigurerAdapter {
#Autowired
private ControllerService controllerService;
#Bean
public Controller controller() {
return new Controller(controllerService);
}
}
}
My theory is that in my test class I am plugging in the wrong content. But why can't we plug in the same content as the one we use from real POST raw payload?
Thanks.

When you use MockMvc, you want to trigger the mapping of your controller, not the HTTP server.
Instead of mockMvc.perform(post("http://testhost/api/configuration/postlist")...
Try mockMvc.perform(post("/configuration/postlist")...

Related

JUnit | MockServer | RestTemplate

I have one service called PostService, which has instance variable called connectionManager Autowired.
Inside this connectionManager there is a one instance variable called restTemplate Autowired.
And in the configuration java file, object of RestTemplate is created with some logic.
When I write a test case around it, and create a MockRestServiceServer with an expected URL and post method, and in return expect a response with some body. and when I execute a test case I don't get mocked response from this mockedRestServiceServer.
As much, I can sense, this is because during test execution, a real object of rest template is created and mock server is not used to send mocked response.
can someone help me, how to overcome from this ?
class PostServie {
#Autowired
private ConnectionManager connectionManager;
public void postMessage(String msg) {
// some logic
}
}
#Component
class ConnectionManager {
#Autowired
private RestTemplate restTemplate;
public String getToken(){
ResponseEntity<String> response = this.restTemplate.postForEntity(url, request, String.class);
//returns response.body() in string format
}
}
#Configuration
class Configuration {
#Bean
public RestTemplate getRestTemplate(){
// some logic and returns object of RestTemplate
}
}
#RunWith(SpringRunner.class)
class PostServiceTest {
#Autowired
private PostMessageService postMessageService;
#Resource(name="authServerRestTemplet")
private RestTemplate authServerRestTemplet;
private MockRestServiceServer mockedAuthServerRestTemplet;
private final String requestToAuthServer ="grant_type=client_credentials&client_id=ceapiClientId";
#Before
public void setUp() {
mockedAuthServerRestTemplet =
MockRestServiceServer.createServer(authServerRestTemplet);
}
#Test
public void postServeiceSuccess () {
mockedAuthServerRestTemplet.expect(requestTo(ACCESS_TOKEN_URI)).andExpect(content().string(requestToAuthServer)).andExpect(method(HttpMethod.POST)).andRespond(withSuccess("{abc : 'abc'}", MediaType.APPLICATION_JSON));
postMessageService.postMessage(jsonMessage);
}
}
}
You're creating an instance of RestTemplate but not using it. What is MockRestServiceServer doing?
I can also see a wrong utilisation of #Resource and #Autowired, which you shouldn't specify in your test. Try use #MockBean.
Autowiring works so that the resource you create in your test is injected in the real class, not the other way round.
I'll just add some sample code here, which might help you achieve your goal.
#RunWith(SpringRunner.class)
#WebMvcTest
public abstract class AbstractControllerTest {
#Autowired
protected MockMvc mockMvc;
#MockBean
protected Service service;
#Before
public void setUp() {
Mockito.reset(service);
}
}
and then your test
public class YourTest extends AbstractControllerTest {
#Test
public void shouldDoSomething() throws Exception {
// given
List<someStuff> stuff = new ArrayList<>();
stuff.add(new Whatever("content", "John Smith");
// when - service is in the abstract controller!
when(service.getSomething()).thenReturn(stuff);
// then
mockMvc.perform(get("/posts/1/whatever").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$[0].content", is("content")))
.andExpect(jsonPath("$[0].author", is("John Smith"))));
}

Junit MockMvc perform POST with path variable in URL return 404 not found

I have a SpringBoot application with this method in the controller to create an user in the database. The controller is working fine in Postman.
#RestController
#RequestMapping("/v1")
public class UserController {
#PostMapping(value = "/user/{id}")
public void createUser(#PathVariable Integer id, #Valid #RequestBody User request,
BindingResult bindingResult) throws Exception {
if (bindingResult.hasErrors()) {
throw new RequestValidationException(VALIDATION_ERRORS, bindingResult.getFieldErrors());
}
userService.createUser(id, request), HttpStatus.CREATED);
}
Now I have a junit test case to test this method and I am getting a 404
#RunWith(SpringRunner.class)
#SpringBootTest(classes = MyApp.class)
public class UserTest {
private MockMvc mockMvc;
final String CREATE_USER_URL = "/v1/user/" + "10";
private final MediaType contentType = new MediaType(MediaType.APPLICATION_JSON.getType(),
MediaType.APPLICATION_JSON.getSubtype(), Charset.forName("utf8"));
#Test
public void testCreateUser() throws Exception {
mockMvc.perform(post(CREATE_USER_URL)
// doesn't work either if I put "/v1/user/10" or post("/v1/user/{id}", 10) here
.content(TestUtils.toJson(request, false))
.contentType(contentType))
.andDo(print())
.andExpect(status().isCreated())
.andReturn();
}
But in the log, I was able to see the correct url:
MockHttpServletRequest:
HTTP Method = POST
Request URI = /v1/user/10
Parameters = {}
Can someone please let me know why I am getting a 404 NOT Found? Thanks.
From docs you need #AutoConfigureMockMvc on class and #Autowire MockMvc
Another useful approach is to not start the server at all, but test only the layer below that, where Spring handles the incoming HTTP request and hands it off to your controller. That way, almost the full stack is used, and your code will be called exactly the same way as if it was processing a real HTTP request, but without the cost of starting the server. To do that we will use Spring’s MockMvc, and we can ask for that to be injected for us by using the #AutoConfigureMockMvc annotation on the test case:
Code :
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
public class UserTest {
#Autowire
private MockMvc mockMvc;
final String CREATE_USER_URL = "/v1/user/" + "10";
private final MediaType contentType = new MediaType(MediaType.APPLICATION_JSON.getType(),
MediaType.APPLICATION_JSON.getSubtype(), Charset.forName("utf8"));
#Test
public void testCreateUser() throws Exception {
mockMvc.perform(post(CREATE_USER_URL)
// doesn't work either if I put "/v1/user/10" or post("/v1/user/{id}", 10) here
.content(TestUtils.toJson(request, false))
.contentType(contentType))
.andDo(print())
.andExpect(status().isCreated())
.andReturn();
}
}
If want to Test your real springboot url Test (End to end Test)
u can use rest-assured or resttemplte
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = Application.class)
#TestPropertySource(value={"classpath:application.properties"})
#SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT)
public class SpringRestControllerTest {
#Value("${server.port}")
int port;
#Test
public void getDataTest() {
get("/api/tdd/responseData").then().assertThat().body("data", equalTo("responseData"));
}
#Before
public void setBaseUri () {
RestAssured.port = port;
RestAssured.baseURI = "http://localhost"; // replace as appropriate
}
}
https://dzone.com/articles/test-driven-development-with-spring-boot-rest-api

How to capture logger.error messages when unit-test Controller with MockMvc?

I have a Spring Boot app and I need to verify that whenever a request failed (different from 200) system logs the status code and message. So far, I cannot find a way to verify logger. Is there any way to do it with Mockito?
MyControllerTest code:
#RunWith(MockitoJUnitRunner.class)
#TestPropertySource(locations = "classpath:application-test.yml")
public class MyControllerTest {
private MockMvc mockMvc;
#Mock
private RestTemplate localRestTemplate;
#InjectMocks
private MyController myController;
#Before
public void setUp() {
mockMvc = MockMvcBuilders.standaloneSetup(myController).build();
}
#Test
public void test() throws Exception {
given(localRestTemplate.getForEntity("/test", String.class))
.willReturn(new ResponseEntity<>("Unsupported media type", HttpStatus.UNSUPPORTED_MEDIA_TYPE));
mockMvc.perform(get(MY_URL))
.andExpect(status().isOk());
}
}
MyController code:
#RestController
#RequestMapping(value = "/my/url")
public class MyController {
protected final static Logger logger = LoggerFactory.getLogger(MyController.class);
private final RestTemplate localRestTemplate;
#Autowired
public MyController(RestTemplate localRestTemplate) {
this.localRestTemplate = localRestTemplate;
}
#Scheduled(cron = "-")
#GetMapping(path="/report")
public void getReport() {
try {
localRestTemplate
.getForEntity("/test", String.class);
} catch (RestClientException e) {
logger.error("Couldn't trigger report generation!", e);
}
}
}
I made research and one of the possible solutions would be through Spy or through changing modifiers to public. Both solutions are not applicable to my current configuration.
Is there anything else I miss that can help me verify logger content or, at least, if logger was called?

Mock a REST request of a child service

I try to test a #RestController within a integration test suite using MockMvc.
#RunWith(SpringRunner.class)
#SpringBootTest
#WebAppConfiguration
public class WebControllerIT {
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
#Test
public void getStatusReurnsSomething() throws Exception {
this.mockMvc.perform(get("/status")).andExpect(status().isOk());
}
}
The #RestController (WebController) calls an injected #Service (RestClientService) which uses RestTemplate to call another REST server. This leads to the following error when running the test.
org.springframework.web.client.ResourceAccessException: I/O error on
GET request for "http://test123.com/42/status": test123.com; nested
exception is java.net.UnknownHostException: test123.com
I used MockRestServiceServer for the integration test of the #Service itself but have no idea how to archieve this within the test of #RestController.
How can I simulate a correct REST call of the RestTemplate?
The #RestController class.
#RestController
public class WebController {
private final RestClientService service;
#Autowired
public WebController(RestClientService service) {this.service = service;}
#GetMapping("/status")
public String getStatus() {
// extract pid from database ...
int pid = 42;
return this.service.getStatus(42);
}
}
The #Serviceclass.
#Service
public class RestClientService {
private final RestTemplate restTemplate;
public RestClientService(RestTemplate restTemplate) {this.restTemplate = restTemplate;}
public String getStatus(int pid) {
String url = String.format("http://test123.com/%d/status", pid);
return this.restTemplate.getForObject(url, String.class);
}
}
Integration/Unit testing doesn't work that way.Objective of this kind of testing is to run through your code and make sure all the business requirement are met but not to hit other system or DB.Here in your case u shouldn't be hitting test123.com to get back data.What needs to done here is that you should mock that method.
public String getStatus(int pid) {
String url = String.format("http://test123.com/%d/status", pid);
return this.restTemplate.getForObject(url, String.class);
}
So that control doesn't enter this method but return you back the mock data(Dummy data).
For example let say that there are 2 kind of status this method is returning and you need to do some business validation based on the string returned.In this case u need to write 2 integration test and make sure the mocking method returns 2 different value(Dummy value instead of hitting that end point)
Reason why we are writing unit testing/integration testing is to make sure your entire code is working as expected but not to hit other system from ur code.
If you want to only test your controller layer, you would do like this.
#RunWith(SpringRunner.class)
#SpringBootTest(classes = MockServletContext.class)
#WebAppConfiguration
public class WebControllerIT {
private MockMvc mockMvc;
private RestClientService service
#Mock
private RestTemplate restTemplate
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
service = new RestClientService(restTemplate);
WebController webController = new WebController(service);
mvc = MockMvcBuilders.standaloneSetup(webController).build();
}
#Test
public void getStatusReurnsSomething() throws Exception {
//Mock the behaviour of restTemplate.
doReturn("someString").when(restTemplate).getForObject(anyString(), anyString());
this.mockMvc.perform(get("/status")).andExpect(status().isOk());
}
}

Spring Boot controller test - assert static HTML response

I am trying to write a unit test to verify that a request returns the content of a static html file. The page is rendered when running the server, but there is no content in the test response.
Controller class:
#Controller
public class IndexController {
#RequestMapping(value = "/", method = RequestMethod.GET)
public String index() {
return "index.html";
}
}
Test class:
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
public class IndexControllerTest {
#Autowired
private MockMvc mockMvc;
#Test
public void shouldReturnIndexPage() throws Exception {
File index = new ClassPathResource("static/index.html").getFile();
String html = new Scanner(index).useDelimiter("\\z").next();
mockMvc.perform(get("/"))
.andExpect(status().isOk())
.andExpect(view().name("index.html"))
.andExpect(content().string(html));
}
}
What am I missing?
EDIT:
I've got a working test which involves actually starting the server. My goal however was to not have to do that, by using #WebMvcTest. Not sure if that's possible or not. I consider this a workaround (unless it's the only way) and am still looking for a solution (that doesn't require starting the server).
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class IndexControllerTest {
#Autowired
private TestRestTemplate restTemplate;
#Test
public void shouldReturnIndexPage() throws Exception {
File index = new ClassPathResource("static/index.html").getFile();
String html = new Scanner(index).useDelimiter("\\z").next();
String responseBody = restTemplate.getForObject("/", String.class);
assertThat(responseBody).isEqualTo(html);
}
}
I came across this question facing the same issue, I managed to build a test using the MockMvc like this
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
public class homePageControllerTest {
#Autowired
private MockMvc mockMvc;
#Test
public void testIndex() throws Exception{
File login = new ClassPathResource("static/login.html").getFile();
String html = new String(Files.readAllBytes(login.toPath()));
this.mockMvc.perform(get("/login.html"))
.andExpect(status().isOk())
.andExpect(content().string(html))
.andDo(print());
}
}
When testing by doing http request, you have to spawn the whole server, so he can handle the request i.e headers, status and such
hope it helps

Categories