I have a gateway controller that uses internally different services. I try to write integration test that mock feign client for the controller but it doesn't work as I expected.
I have the following Feign client:
public interface StoreManagementClient {
#RequestLine("GET /v1/stores/{storeId}")
#Headers({"Accept: application/json", "Content-Type: application/json;charset=UTF-8"})
StoreDetails getStoreDetails(#Param("storeId") String storeId);
}
Store controller:
#Validated
#Controller
#RequestMapping("${gateway.path}")
public class StoreController {
#Autowired
private StoreManagementClient storeManagementClient;
#GetMapping(value = "/stores/{storeId}", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<StoreDetails> getStoreDetails(
#PathVariable("storeId") String storeId) {
StoreDetails details = storeManagementClient.getStoreDetails(storeId);
return ResponseEntity.ok(details);
}
}
And the integration test:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {GatewayServiceApplication.class},
webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
public class ClientIntegrationTest {
#Autowired
private StoreController storeController;
#MockBean
private StoreManagementClient storeManagementClient;
private MockClient mockClient;
#Before
public void setUp() throws Exception {
mockClient = new MockClient();
}
#Test
public void testCorrectGetStoreDetailsRequest() throws JsonProcessingException {
String storeId = "store-1";
StoreDetails storeDetails = new StoreDetails();
storeDetails.setId(storeId);
storeDetails.setType("grocery");
String response = new ObjectMapper().writeValueAsString(storeDetails);
storeManagementClient = Feign.builder()
.encoder(new JacksonEncoder())
.decoder(new JacksonDecoder())
.client(mockClient
.ok(RequestKey.builder(feign.mock.HttpMethod.GET, "/v1/stores/" + sroreId)
.headers(ImmutableMap.of(
ACCEPT, newArrayList("application/json"),
CONTENT_TYPE, newArrayList("application/json;charset=UTF-8"))).build(),
response
))
.target(new MockTarget<>(StoreManagementClient.class));
// when
ResponseEntity<StoreDetails> result = storeController.getStoreDetails(storeId);
// then
StoreDetails resultBody = result.getBody();
assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(resultBody.getId()).isEqualTo(storeId);
assertThat(resultBody.getType()).isEqualTo("grocery");
}
I suppose that test should mock response according the described Feign Client. But actually it returns null.
Should I do something wrong with mocking Feign client? Probably, I mixed in one test testing Feign client and my own controller and I need to separate it and write unit-test for Feign client like Mock Feign Client example?
I would be appreciate any advice
First, you replace feign client StoreManagementClient by mock:
#MockBean
private StoreManagementClient storeManagementClient;
Then in test you lost reference to the mock and points to local object:
storeManagementClient = Feign.builder()....build();
but controller still use the mock
In Plain java, you do something like:
Client client = new Client(null);
Controller controller = new Controller(client);
client = new Client(1);
assertThat(controller.get()).isEqualTo(1)) // no no no: is equal to null
PS. I hope in future my answer will be constructive
Related
I am trying to write an Integration test where I have an issue in mocking the rest call which is calling outside server using JUnit.
I have added a #Mock and #InjectMock on service
Service looks like this.
#Service
public class BoundaryDeltaService {
private BoundaryDelta getBoundaryDeltaUsingApp() {
List<BoundaryValueInfo> infoFromSource = Arrays.asList(serviceAdapter.readInfoFromApiUsingApp(boundarySet, app, loginUserInfo));
return getBoundaryDeltacompareCurrentBoundaryValuesWithSource(boundarySet, infoFromSource );
}
}
There is another service with has restTemplate call
#Service
public class ServiceAdapter {
public BoundaryValueInfo[] readInfoFromApiUsingApp(){
String loginToken = systemUserLoginService.getSystemUserTokenManual(app, loginUserInfo);
restTemplate = createRestTemplate(boundarySet);
HttpHeaders headers = new HttpHeaders() {{
String authHeader = "Bearer " + loginToken;
set( "Authorization", authHeader );
}};
HttpEntity<String> request = new HttpEntity<String>(headers);
ResponseEntity<BoundaryValueInfo[]> answerFromApi = restTemplate.exchange(boundarySet.getApiUri(), HttpMethod.GET, request, BoundaryValueInfo[].class);
return getResponseFromApi(answerFromApi);
}
}
And this is the test scenario
#RunWith(SpringJUnit4ClassRunner.class)
#ActiveProfiles({"aws", "local"})
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = FlywayConfig.class)
#DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS)
public class BoundaryValueDeltaControllerTest {
private static final String API_V1 = "/api/v1/";
#Autowired
TestRestTemplate testRestTemplate;
#Autowired
private BoundaryDeltaService boundaryDeltaService;
#Autowired
private DomainBuilder domainBuilder;
#Autowired
private AppBuilder appBuilder;
#Autowired
private AppAdminBuilder appAdminBuilder;
#Autowired
private BoundaryValueBuilder boundaryValueBuilder;
#Autowired
private BoundarySetBuilder boundarySetBuilder;
#MockBean
private LoginUserProvider loginUserProvider;
#MockBean
private LoginTokenService loginTokenService;
#InjectMocks
private BoundaryServiceAdapter boundaryServiceAdapter;
#Mock
RestTemplate restTemplate;
#LocalServerPort
private int port;
Domain domain;
App app;
BoundarySet boundarySet;
BoundaryValue boundaryValue;
LoginUserInfo loggedInUser;
#Before
public void setUp() {
clear();
domain = domainBuilder.persist();
app = appBuilder.persist(domain);
boundarySet = boundarySetBuilder.persist(domain);
boundaryValue = boundaryValueBuilder.persist(boundarySet);
}
#After
public void tearDown() {
clear();
}
#BeforeClass
public static void setupTestEnv() {
// https://github.com/localstack/localstack/issues/592
}
#Test
public void updateBoundaryValuesFromApi() {
aLoggedInUser(domain.getAuthor().getUsername());
appAdminBuilder.persist(app, domain.getAuthor());
ResponseEntity<BoundaryValueInfo[]> answerFromApi = getBoundaryValueInfos();
HttpHeaders headers = new HttpHeaders() {{
String authHeader = "Bearer 1234";
set( "Authorization", authHeader );
}};
HttpEntity<String> request = new HttpEntity<>(headers);
//when(restTemplate.exchange(boundarySet.getApiUri(), HttpMethod.GET, request, BoundaryValueInfo[].class)).thenReturn(answerFromApi);
when(restTemplate.exchange(ArgumentMatchers.anyString(),
ArgumentMatchers.any(HttpMethod.class),
ArgumentMatchers.any(),
ArgumentMatchers.<Class<BoundaryValueInfo[]>>any())
).thenReturn(answerFromApi);
String url = url(API_V1 + "domains/" + domain.getName() + "/boundarysets/" + boundarySet.getBoundarySetName() + "/app/" + app.getName()+ "/updatefromapi/");
ResponseEntity<String> response = testRestTemplate.exchange(url,HttpMethod.GET, null, String.class);
assertEquals(HttpStatus.OK, response.getStatusCode());
}
}
I am calling controller with api and from there it is going into above services which has rest call but not able to mock the actual call.
Can someone guide me here ?
You are not using the TestRestTemplate for the purpose it is intended for.
TestRestTemplate is not an extension of RestTemplate, but rather an alternative that simplifies integration testing and facilitates authentication during tests. It helps in customization of Apache HTTP client, but also it can be used as a wrapper of RestTemplate
Solution:
Approach1:
1) Directly call the exchange method using RestTemplate object.
ResponseEntity<String> response = restTemplate .exchange(url,HttpMethod.GET, null, String.class);
2) Mock the testRestTemplate and do the mock when on the exchange method call for this object.
when(testRestTemplate.exchange(ArgumentMatchers.anyString(),
ArgumentMatchers.any(HttpMethod.class),
ArgumentMatchers.any(),
ArgumentMatchers.<Class<BoundaryValueInfo[]>>any())
).thenReturn(answerFromApi);
I am trying to test my REST endpoints using RestAssured with mocking some of the service/repositories in the controller.
this is my test class:
#RunWith(SpringJUnit4ClassRunner.class)
#Transactional
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = {VedicaConfig.class})
#AutoConfigureMockMvc
#ActiveProfiles("test")
public class RESTTest {
#LocalServerPort
private int port;
#Autowired
private MockMvc mvc;
#Mock
MetaVersionDAO metaVersionDAO;
#InjectMocks
DocCtrl docCtrl;
#Before
public void contextLoads() {
RestAssured.port = port;
assertThat(mvc).isNotNull();
// this must be called for the #Mock annotations above to be processed.
MockitoAnnotations.initMocks(this);
RestAssuredMockMvc.standaloneSetup(MockMvcBuilders.standaloneSetup(docCtrl));
}
#Test
public void shouldGetThumbnail() {
String ver = "1.0";
String uuid = "124-wqer-365-asdf";
when(metaVersionDAO.getMetaByVersionUUID(ver, uuid)).thenReturn(new DocVersion());
given()
.when()
.param("uuid", uuid)
.param("versionVed", ver)
.get(CTX_BASE + "/thumbnail")
.then()
.log().ifValidationFails()
.statusCode(OK.value())
.contentType(ContentType.BINARY);
}
}
now, the REST endpoint itself is being hit correctly with supplied parameters. this endpoint has DocCtrl injected which uses metaVersionDAO instance in turn:
public RawDocument getDocThumbnail(String uuid, String versionVed) throws Exception {
DocVersion docVersion = metaVersionDAO.getMetaByVersionUUID(versionVed, uuid);
InputStream inputStream = okmWebSrv.getOkmService().getContentByVersion(uuid, versionVed);
String dataType = docVersion.getMetadata().getAdditionals().get(Vedantas.CONTENT_TYPE);
ByteArrayInputStream bais = new ByteArrayInputStream(createPDFThumbnail(dataType, inputStream));
RawDocument rawDocument = new RawDocument(bais, "qwer");
return rawDocument;
}
as you can see, I have tried to mock metaVersionDAO at the top of the #Test method so I expected it to return new DocVersion() as I set it to, but in this DAO the actual code is being called and it fails on entityManager which is null.
My question is why metaVersionDAO.getMetaByVersionUUID doesn't return my mocked object and what should I do to make it so?
spring-mock-mvc: 3.3.0
spring-boot: 2.1.2.RELEASE
Thanks!
solved by changing #Mock for #MockBean.
so it is:
#MockBean
MetaVersionDAO metaVersionDAO;
everything else remains the same as in the post and it uses mocked instance.
I have a method in the service class that makes a call to an external api. How would I mock this okHttpClient call? I have tried to do so with mockito but no luck with that.
//this is the format of the method that i want to test
public string sendMess(EventObj event) {
OkHttpClient client = new OkHttpClient();
//build payload using the information stored in the payload object
ResponseBody body =
RequestBody.create(MediaType.parse("application/json"), payload);
Request request = //built using the Requestbody
//trying to mock a response from execute
Response response = client.newCall(request).execute();
//other logic
}
I am open to refactoring the service class if it helps with the testing. Any suggestions and recommendation is appreciated. Thanks.
Since you are using spring-boot leave managing beans to spring.
1) First create OkHttpClient as spring bean so that you can use it all over application
#Configuration
public class Config {
#Bean
public OkHttpClient okHttpClient() {
return new OkHttpClient();
}
}
2) And then in the service class #Autowire OkHttpClient and use it
#Service
public class SendMsgService {
#Autowired
private OkHttpClient okHttpClient;
public string sendMess(EventObj event) {
ResponseBody body = RequestBody.create(MediaType.parse("application/json"), payload);
Request request = //built using the Requestbody
//trying to mock a response from execute
Response response = okHttpClient.newCall(request).execute();
//other logic
}
}
Tests
3) Now in the Test classes use #SpringBootTest,#RunWith(SpringRunner.class) and #MockBean
The #SpringBootTest annotation can be used when we need to bootstrap the entire container. The annotation works by creating the ApplicationContext that will be utilized in our tests.
#RunWith(SpringRunner.class) is used to provide a bridge between Spring Boot test features and JUnit. Whenever we are using any Spring Boot testing features in out JUnit tests, this annotation will be required.
#MockBean Annotation that can be used to add mocks to a Spring ApplicationContext.
#SpringBootTest
#RunWith(SpringRunner.class)
public class ServiceTest {
#Autowire
private SendMsgService sendMsgService;
#MockBean
private OkHttpClient okHttpClient;
#Test
public void testSendMsg(){
given(this.okHttpClient.newCall(ArgumentMatchers.any())
.execute()).willReturn(String);
EventObj event = //event object
String result = sendMsgService.sendMess(event);
}
}
I would suggest, that you pull out the instantiation of your OkHttpClient to an own method in a Configuration class. Afterwards you could #Inject the client anywhere it is needed and testing becomes much easier because you can #Mock it away.
Such to say the Spring-managed bean:
#Configuration
public class OkHttpClientConfiguration {
#Bean
public OkHttpClient okHttpClient() {
return new OkHttpClient();
}
}
…your production class:
#Component
public class ProductionClass {
#Inject
private OkHttpClient okHttpClient;
public string sendMess(EventObj event) {
okHttpClient // whatever you want
[…]
}
}
…and your test:
public class SpyTest {
#InjectMocks
private ProductionClass productionClass;
#Mock
private OkHttpClient okHttpClient;
#Before
public void initMocks() {
MockitoAnnotations.initMocks(this);
}
#Test
public void spyInsteadOfPowermock() {
Request request = // mock the request
when(okHttpClient.newCall(request)).thenReturn(mock(Call.class));
}
}
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 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