I have a Circuit breaker implemented which works fine when I run it (meaning the fallback method is run whenever the RestTemplate receives an HTTP status code between 400 and 599). However, when I try to unit test this fallback, by returning a Bad Request (HTTP 400) the fallback method is not invoked. Why is this?
Snippet from class:
class Test {
#Autowired
private RestTemplate restTemplate;
#HystrixCommand(fallbackMethod = "fallback")
public void test() {
HttpEntity<Object> testRequest = new HttpEntity<>();
ResponseEntity<String> response = restTemplate.exchange(
"http://localhost:8080/testurl",
HttpMethod.POST,
testRequest,
String.class);
}
private void fallback() {
System.out.println("Fallback method called");
}
}
Snippet from test class
#MockBean
private RestTemplate mockRestTemplate;
#Autowired
Test test;
#Test
public void testRestTemplateReturning400() {
ResponseEntity<String> response = new ResponseEntity<>(HttpStatus.BAD_REQUEST);
when(mockRestTemplate.exchange(anyString(), any(), any(), eq(String.class))).thenReturn(response);
test.test();
verify(mockRestTemplate, times(1)).exchange(anyString(), any(), any(), eq(String.class));
}
Add
#EnableCircuitBreaker
#EnableAspectJAutoProxy
to your test configuration
Related
for a spring boot application that needs to be tested below is my query.
#CustomLog
#RestController
#RequestMapping("/my_path")
public class MyController {
#GetMapping(path = "**", produces = {MediaType.APPLICATION_JSON_VALUE})
public ResponseEntity<JsonNode> fetchData(HttpServletRequest request){
... some code.....which also calls external apis.....
}
#PostMapping(path = "**", produces = {MediaType.APPLICATION_JSON_VALUE})
#ResponseBody
public ResponseEntity<Map<String, Object>> createMOI(HttpServletRequest request){
... some code.....which also calls external apis.....
}
}
My application calls an external service which now needs to be mocked.
this.webClient = WebClient.builder().baseUrl("http://localhost:9600/external_host_path")
.defaultHeader(HttpHeaders.CONTENT_TYPE,MediaType.APPLICATION_JSON_VALUE)
.build();
Mono<Pojo>responseMo = webClient.post().uri("/aGivenSubPath")
.accept(MediaType.APPLICATION_JSON).bodyValue(requestPoJo)
.retrieve().bodyToMono(Pojo.class).block();
I am calling my controller API with MVC as part of springtest
#RunWith(SpringRunner.class)
#SpringBootTest
public class MyControllerTest {
#Autowired
MyController controller;
#Before
public void setup() throws Exception {
this.mockMvc = standaloneSetup(this.controller).build();
}
#Test
public void testControl() throws Exception {
mockMvc
.perform(post("http://localhost:9600/my_path")
.contentType(MediaType.APPLICATION_JSON)
.content("{'someData':'[]'}"))
.andExpect(status().isAccepted())
.andReturn();
}
}
What I am looking for is to somehow proxy or side effect
http://localhost:9600/external_host_path
and redirect all calls made for this host to a custom Utility class which provides response based on the request params to the external host programatically.
I have seen multiple examples for mockito, wireMock, mockwebserver, mockserver etc
But most of them work on a given(static path)-when(static path called)-then(give static response).
I have many calls through out the flow and I already have the logic of the utility class to generate responses for provided request arguments.
Although I was not able to find a answer to redirect webserver request to sideEffect class,
For now atleast managing by Mockito's MockBean and Answer.
#RunWith(SpringRunner.class)
#SpringBootTest
public class MyControllerTest {
#Autowired
MyController controller;
#MockBean
MyExternalServiceClient serviceClient;
#Autowired
MySideEffectService sideEffect;
#Before
public void setup() throws Exception {
this.mockMvc = standaloneSetup(this.controller).build();
Mockito.when(serviceClient.getMethod(any(),anyBoolean())).thenAnswer((Answer) invocation -> {
Object[] args = invocation.getArguments();
Object mock = invocation.getMock();
return sideEffect.getMethod((Map<String, List<String>>) args[0], (Boolean) args[1]);
});
}
#Test
public void testControl() throws Exception {
mockMvc
.perform(post("http://localhost:9600/my_path")
.contentType(MediaType.APPLICATION_JSON)
.content("{'someData':'[]'}"))
.andExpect(status().isAccepted())
.andReturn();
}
}
Will still have a lookout for a way (Maybe TestContainers with image creation on the fly that will create a server with my mockCode, so that i can use hostname of this one and replace with existing real hostname)
I'm developing a REST API with Spring Boot.
I have a controller to create a new user, that responds with 201 (CREATED) when the user is created. The response has no body content.
Using Postman, or any browser, I got a 201 response.
But when I try with unit test (Mockito), the response is 200.
Here is my code:
Controller:
public CompletableFuture<ResponseEntity<Void>> registerNewUser(
#RequestBody #Valid RegisterUserDto newUser
) throws ExecutionException, InterruptedException {
// user service return a completable future void
return userService.registerNewUser(newUser).thenApply(u -> new ResponseEntity<>(u, HttpStatus.CREATED));
}
The user service returns a completable future void when the register process is completed.
#Async
CompletableFuture<Void> registerNewUser(NewUserDto newUserDto) throws ExecutionException, InterruptedException;
Then, in my unit test, I have the following code:
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
public class UsersControllerTest {
#Autowired
private MockMvc mvc;
#Mock
private UsersService userService;
#InjectMocks
private UsersControllers usersController;
#Before
public void init() {
MockitoAnnotations.initMocks(this);
this.mvc = MockMvcBuilders.standaloneSetup(usersController).build();
}
#Test
public void mustCreateANewUser() throws Exception {
NewUserDto userMock = new NewUserDto("firstname", "lastname", "login", "password");
when(userService.registerNewUser(any(NewUserDto.class)))
.thenReturn(CompletableFuture.completedFuture(null));
mvc.perform(post("/api/users/new")
.content(TestHelpers.convertToJson(userMock))
.contentType(TestHelpers.getJsonMediaType()))
.andExpect(status().isCreated());
}
}
TestHelpers.convertToJson and TestHelpers.getJsonMediaType are static methods.
public static MediaType getJsonMediaType() {
return new MediaType(MediaType.APPLICATION_JSON.getType(),
MediaType.APPLICATION_JSON.getSubtype(),
Charset.forName("utf8"));
}
public static String convertToJson(Object o) throws IOException {
ObjectMapper mapper = new ObjectMapper();
return mapper.writeValueAsString(o);
}
I do not understand why the response code was 200 on unit test. In any part of my controller, service, or controller advice I have a response 200 OK.
The problem was because my controller and service are async, so my unit test it's not waiting for the correct response.
Changed my unit test to:
MvcResult result = mvc.perform(post("/api/users/new")
.content(TestHelpers.convertToJson(registroMock))
.contentType(TestHelpers.getJsonMediaType()))
.andReturn();
mvc.perform(asyncDispatch(result))
.andExpect(status().isCreated());
I am attempting to write a unit test for a generic service class like the following:
public class ApiService{
private RestTemplate restTemplate;
private ServiceDao serviceDao;
#Autowired
public ApiService(RestTemplate restTemplate, ServiceDao serviceDao) {
this.restTemplate = restTemplate;
this.serviceDao = serviceDao;
}
public ResponseEntity getObject(ObjectRequest request) {
// Service logic here
}
public ResponseEntity postObject(CreateObjectRequest request) {
// Service logic here
}
}
But am struggling with how to mock the restTemplate in the constructor of my service class such that when the test runs, data is not persisted.. I've looked into Mockito though don't see many examples or documentation regarding Mockito + TestNG in this context. Any help would be appreciated
First of all - if possible inject RestOperations in your service instead of RestTemplate. Then you will be able easily mock its behavior (note: RestTemplate implements RestOperations).
If using RestOperations is not possible - you can do something this:
RestTemplate myTemplate = Mockito.spy(new RestTemplate());
String expectedOutput = "hello mock";
String inputUrl = "https://stackoverflow.com/questions/53872148/unit-test-service-class-with-mocks-using-testng";
Mockito.doReturn(expectedOutput).when(myTemplate).getForObject(inputUrl, String.class);
String result = myTemplate.getForObject(inputUrl, String.class);
Assert.assertEquals(expectedOutput, result);
I've actually constructed a method using Mockito as follows... there might be a more elegant solution so I would be interested to see:
public class ServiceTest {
#BeforeMethod(groups="serviceTest")
public void beforeMethod() {
MockitoAnnotations.initMocks(this);
}
#Test(groups="serviceTest")
public void testGetService_returns200() {
when(serviceDao.getService(any(String.class), any(RestTemplate.class), any(HttpHeaders.class))).thenReturn(new ResponseEntity(new Object(), HttpStatus.OK));
ObjectRequest request = new ObjectRequest();
// set request values
ResponseEntity testResponse = apiService.getObject(request);
}
}
I want to test a Tasklet implementation which uses an #Autowired RestTemplateBuilder to build a RestTemplate. The RestTemplate executes a request. I want to mock the response of this request.
#ContextConfiguration(classes = DogTasklet.class )
#RunWith(SpringRunner.class)
public class DogTaskletTest {
#MockBean
RestTemplateBuilder restTemplateBuilder;
private RestTemplate restTemplate = new RestTemplate();
#Autowired
private Tasklet sut;
#Before
public void setUp() throws Exception {
given(this.restTemplateBuilder.build()).willReturn(restTemplate);
}
}
The given() statement throws a NPE because the RestTemplateBuilder instance is null. What have I missed?
Update: I changed the test to the following which solves the NPE, now I have null ResponseEntity during sut.execute().
#RunWith(SpringRunner.class)
public class DogTaskletTest {
#TestConfiguration
static class TestConfig {
#Bean
RestTemplateBuilder restTemplateBuilder() {
RestTemplateBuilder restTemplateBuilder = mock(RestTemplateBuilder.class);
RestTemplate restTemplate = mock(RestTemplate.class);
ResponseEntity responseEntity = mock(ResponseEntity.class);
given(restTemplateBuilder.build()).willReturn(restTemplate);
given(restTemplate.execute(any(), any(), any(), any())).willReturn(responseEntity);
given(responseEntity.getBody()).willReturn("{}");
return restTemplateBuilder;
}
#Bean
DogTasklet sut() {
return new DogTasklet("string", restTemplateBuilder());
}
}
#Test
public void execute() throws Exception {
// when
sut.execute(stepContribution, chunkContext);
}
}
Thanks to Urosh I figured out that I was mocking the wrong method in my given() statement. Therefore it did not return the mocked RestTemplate.
I changed the given() to:
given(restTemplate.exchange(
anyString(),
eq(HttpMethod.GET),
any(HttpEntity.class),
eq(String.class)
)).willReturn(responseEntity);
I am trying to test a RestTemplate exchange with a response entity in a controller from a rest Service in another application. My Junit test is coming back with a null ResponseEntity. I have tried a couple ways with no luck. First tried using mockito to mock response methods (when...then). Second tried with Exchange matchers. I also tried to mix the two with no luck. Any help would be great. Here is my Controller response I am creating:
ResponseEntity<RestResult<List<SiteMessage>>> response = rest.exchange(getAllMessagesUrl, HttpMethod.GET,
HttpEntity.EMPTY, new ParameterizedTypeReference<RestResult<List<SiteMessage>>>() {
});
Here is my Junit Test:
#RunWith(MockitoJUnitRunner.class)
#SpringBootTest
public class MessageControllerTests {
#InjectMocks
MessageController messageController;
#Mock
RestTemplate restTemplate;
#Mock
SiteMessageService serviceMock;
#Mock
ResponseEntity<RestResult<List<SiteMessage>>> response;
private MockMvc mockMvc;
#Before
public void setUp() throws Exception {
mockMvc = MockMvcBuilders.standaloneSetup(messageController).build();
}
#Test
public void testListPage() throws Exception {
RestResult<List<SiteMessage>> result = new RestResult<>();
result.setMessage("success");
SiteMessage message1 = new SiteMessage();
SiteMessage message2 = new SiteMessage();
message1.setId("ldsf030j2ldjs");
message2.setId("ldsf0432234s");
List<SiteMessage> messageList = new ArrayList<>();
messageList.add(message1);
messageList.add(message2);
result.setResults(messageList);
when(response.getBody()).thenReturn(result);
when(response.getStatusCode()).thenReturn(HttpStatus.NOT_FOUND);
when(restTemplate.exchange(
Matchers.any(URI.class),
Matchers.eq(HttpMethod.GET),
Matchers.any(),
Matchers.<ParameterizedTypeReference<RestResult<List<SiteMessage>>>>any())
).thenReturn(response);
mockMvc.perform(get("/message/list")).andExpect(status().isOk()).andExpect(view().name("message/list"));
}
}
I am trying to return a response with a body containing a RestResult object, which has a list of messages and a message