Spring boot WireMock junit5 not mocking an external call - java

My FristService is internally calling a SecondService through feign client, and I am trying to write a test for FirstService and want to mock the call to the second service.
It seems like wiremock is unable to intercept and respond with the mock results but throws the following exception (as it's not mocking)
java.lang.RuntimeException: com.netflix.client.ClientException: Load balancer does not have available server for client: second-service
at org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient.execute(LoadBalancerFeignClient.java:90)
at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:119)
Here is the Test code
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#ContextConfiguration(initializers = {WireMockInitializer.class})
#AutoConfigureMockMvc
public class TestFirstController {
#Autowired
private WireMockServer wireMockServer;
#Inject
public MockMvc mockMvc;
#LocalServerPort
private Integer port;
#AfterEach
public void afterEach() {
this.wireMockServer.resetAll();
}
#Test
public void testSomeMethod() throws Exception {
this.wireMockServer.stubFor(
WireMock.get(urlPathMatching("/second-controller/1"))
.willReturn(aResponse()
.withHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE)
.withBody("[{\"id\":1,\"message\":\"Child title 1\"}]"))
);
mockMvc.perform(MockMvcRequestBuilders.request(HttpMethod.GET, "/first-controller/1"))
.andDo(MockMvcResultHandlers.print())
.andExpect(status().isOk());
}
}
here is my business method that first fetches record from the database and then makes a call to the second service
#Override
public First getFirst(Integer id) {
First first = map(respository.findById(id));
//feign-client call
List<Second> seconds = client.getSeconds(id);
first.setChild(seconds);
return first;
}
and here is my feign client and I am not changing anything about it in test mode
#FeignClient("service-2")
public interface SecondApi {
#GetMapping({"/second-controller/{id}"})
SomeData getData(#PathVariable("id") Integer id);
}

Related

how can i insert advanced data in spring boot test?

I'm making test code in spring boot.
But, my test code doesn't save the data using #Before method.
If i request to '/v1/stay/, it return empty array...
Please can you explain what is wrong with my code?
Here is my test code.
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
public class StayControllerTest {
#MockBean
private StayService stayService;
#Autowired
private MockMvc mockMvc;
// givenStay method is the method generating dummy data
#Before
public void before() {
stayService.save(givenStay1());
stayService.save(givenStay2());
stayService.save(givenStay3());
stayService.save(givenStay4());
stayService.save(givenStay5());
}
#Test
#Transactional
void showStayList() throws Exception {
List<StayReq> original = new ArrayList<>();
original.add(givenStay1());
original.add(givenStay2());
original.add(givenStay3());
original.add(givenStay4());
original.add(givenStay5());
MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get("/v1/stay")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andDo(print())
.andReturn();
System.out.println(result.getResponse());
}
}
And below code blocks are my StayController and StayService
#RestController
#ApiV1
#RequiredArgsConstructor
public class StayController {
private final StayService stayService;
private final ApiService apiService;
#GetMapping("/stay")
public ResponseEntity<Response> stayList() {
return apiService.okResponse(stayService.getList());
}
}
#Service
#RequiredArgsConstructor
public class StayService {
private final StayRepository stayRepository;
private final RoomRepository roomRepository;
public List<StayRes> getList() {
return stayRepository.findAll().stream().map(StayRes::new).collect(Collectors.toList());
}
#Transactional
public void save(StayReq stayReq) {
stayRepository.save(stayReq.toEntity());
}
}
You injected a mock, not a 'real' service. If you want to use a 'real' service - you need to replace #MockBean annotation with #Autowired annotation.
Or alternatively - you can configure mock in the test method to return some predefined data.

Re-direct requests to SideEffect Utility Classes

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)

Spring Boot controller test loading entire application context

Spring Boot here. I currently have the following REST controller:
#RestController
public class FizzbuzzController {
private final FizzbuzzService FizzbuzzService;
public FizzbuzzController(FizzbuzzService FizzbuzzService) {
this.FizzbuzzService = FizzbuzzService;
}
#PostMapping("/Fizzbuzzs/{fizzbuzzId}")
public ResponseEntity<FizzbuzzDTO> addFizzbuzz(#RequestParam("files") List<MultipartFile> files,
#PathVariable String fizzbuzzId) throws IOException {
FizzbuzzDTO fizzbuzzDTO = fizzbuzzService.store(files, fizzbuzzId);
return ResponseEntity.status(HttpStatus.OK).body(fizzbuzzDTO);
}
}
I would like to write an integration test for it that:
Mocks or stubs an HTTP request to the URL; and
Allows me to inject the FizzbuzzController (under test) with a mocked FizzbuzzService or the real thing; and
Allows me to inspect the HTTP response coming back from the method (check status code, check response entity, etc.)
My best attempt thus far:
#WebMvcTest(FizzbuzzController.class)
#EnableConfigurationProperties
#AutoConfigureMockMvc
public class FizzbuzzControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private FizzbuzzService FizzbuzzService;
#Test
public void should_store_fizzbuzz_files() throws Exception {
// I can't even get the test to run
assertTrue(1 == 1);
}
}
When I run this, the test fails to run and it is clear (looking at the logs) that Spring is loading the entire application context of my app, whereas I just want it to isolate the context to this test class, the main FizzbuzzController class, and anything in the dependency tree underneath it.
Can anyone spot where I'm going awry?
You need another context for testing. I'm suggesting you to have a separate Test config:
#TestConfiguration
#Slf4j
#EnableJpaRepositories("tth.patientportal.repository")
public class TestConfig { // bean configs goes here for testing if you need to change
// context}
and in a controller test build the context like below:
#RunWith(SpringRunner.class)
#AutoConfigureTestEntityManager
#SpringBootTest
#TestPropertySource("classpath:application-unittest.properties")
#ContextConfiguration(classes = {TestConfig.class})
public class RestControllerTest {
#Autowired
private WebApplicationContext webApplicationContext;
private MockMvc mockMvc;
#Before
public void setup()
{
mockMvc = MockMvcBuilders.
webAppContextSetup(webApplicationContext)
.build();
}
#Test
public void shouldReturnRegisteredUser() throws Exception {
this.mockMvc.
perform(MockMvcRequestBuilders
.post("url")
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("$.username").exists());
}
}

Should we test getForObject?

I am writing a simple REST client, my service class has one method using
RestTemplate.getForObject()
I would like to practice testing but I don't really know if we should test the class. I also do not know how to test method that does not do much. Should I write any unit tests?
You can test, that request has been sent. For example you have the next service:
#Service
public void QueryService {
private final RestTemplate restTemplate;
#Autowired
public QueryService(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
public List<Employee> void makeQuery() {
ResponseEntity<List<Employee>> response = restTemplate.getForObject(
"http://localhost:8080/employees",
EmployeeList.class);
return response.getEmployees();
}
}
For the method makeQuery you can write the next unit test:
#RunWith(MockitoJUnitRunner.class)
public class QueryServiceTest {
#Mock
private RestTemplate restTemplate;
#InjectMocks
private QueryService queryService;
#Test
public void testMakeQueryRequestHasBeenSent() {
List<Employee> result = queryService.makeQuery();
// This string checks that method getForObject has been invoked
verify(restTemplate).getForObject(anyString(), any(Class.class));
}
}
In the future, if you will change makeQuery method and forget to send request using restTemplate, this test will fail.

How do I prevent RestAssured from overwriting mocks with real implementations?

I'm trying to test a Jersey REST service endpoint with RestAssured while mocking its dependency. I use Spring to autowire the endpoint, and I set a mock dependency before executing the test. However, when RestAssured calls the service, it gets re-autowired again, and my mock is replaced with the real implementation. How do I prevent that?
I'm using Spring Boot, Jersey, EasyMock and RestAssured.
My endpoint class:
#Component
#Path("foo")
#Produces(MediaType.TEXT_PLAIN)
public class FooEndpoint {
private FooService service;
#GET
#Produces({MediaType.TEXT_PLAIN})
public String get() {
return service.getFoo();
}
#Autowired
public void setService(FooService service) {
System.out.println(String.format("<<< Endpoint %s. Setting service %s", this, service));
this.service = service;
}
}
FooService:
#Service
public class FooService {
public String getFoo() {
return "real foo";
}
}
Test:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = {Application.class}, webEnvironment = WebEnvironment.RANDOM_PORT)
public class FooEndpointIT {
#LocalServerPort
private int port;
#Autowired
private FooEndpoint target;
private FooService serviceMock;
#Test
public void testGet() {
RestAssured.port = port;
String expected = "mock foo";
serviceMock = EasyMock.strictMock(FooService.class);
target.setService(serviceMock);
EasyMock.expect(serviceMock.getFoo()).andReturn(expected);
EasyMock.replay(serviceMock);
String actual = RestAssured.get("foo").body().asString();
System.out.println(actual);
assertEquals("mock foo", actual); // Fails: expected:<[mock] foo> but was:<[real] foo>
}
}
And in the logs I see this:
<<< Endpoint com.foo.foo.FooEndpoint#6972c30a. Setting service com.foo.foo.FooService#57a48985
. . . Tomcat initializes and starts . . .
<<< Endpoint com.foo.foo.FooEndpoint#6972c30a. Setting service EasyMock for class com.foo.foo.FooService
<<< Endpoint com.foo.foo.FooEndpoint#6972c30a. Setting service com.foo.foo.FooService#57a48985
real foo
How do I prevent the FooEndpoint from being re-autowired after I set my mock?

Categories

Resources