Test service with 3rd party API calls - java

I have a service with business logic and http calls (using Unirest) to a third party service.
public PaymentLinkResult generatePaymentLink(PaymentLinkParams paymentLinkParams) throws GatewayException {
// BUSINESS LOGIC
HttpResponse<JsonNode> response;
try (UnirestInstance service = Unirest.spawnInstance()) {
response = service.post(apiBaseUrl + "/api/v1/openOrder.do")
.body(body)
.asJson();
}
if (response.getBody() == null) {
// BUSINESS LOGIC
}
// BUSINESS LOGIC
}
I created a test for this service, my goal is to test the business logic without making a real HTTP call to the 3rd party service. Basically i want to test if the request is created correctly and if the output is correct.
I'm struggling to find the best way to handle this scenario with Spring Boot. I know how to mock the entire method but i would like to mock only the HTTP call part.

Related

Regarding using mock stripe for e2e tests

I came across stripe mock(https://github.com/stripe/stripe-mock) recently, for local testing,
public PaymentMethod createPaymentMethod(Card card) {
var paymentMethodParams =
PaymentMethodCreateParams.builder()
.setType(Type.CARD)
.setCard(
CardDetails.builder()
.setNumber(card.getNumber())
.setExpMonth((long) card.getExpiryMonth())
.setExpYear((long) card.getExpiryYear())
.setCvc(card.getCvv())
.build())
.build();
var response = testMode? "Make API call to mock stripe with params" : PaymentMethod.create(paymentMethodParams);
return response;
}
Now I do not want to manually make http call to stripe mock server with http client and request body, Is there a way to change something in RequestOptions or use a different dependency to make the call?
Basically, I dont want to do, something like
String url = String.format("%s%s", Stripe.getApiBase(), String.format("/v1/payment_methods/%s", ApiResource.urlEncodeId(paymentMethod)));
just somehow override the stripe base API URL.
Stripe.overrideApiBase(); is something useful, but its static to stripe java library, I want normal stripe calls to go through, when my e2e tests are running.
Stripe.overrideApiBase() is likely what you want; you'd just want to configure your app to only override it when running end to end tests.

How to mock response of an external API called from within an internal API integration test

I am writing integration tests for an application that uses an internal REST API to perform an action; all Java.
In the API, there is a POST method that calls upon an external API. In the test, I need to send a request to my API to perform an action.
The problem is, I do not want to send real requests to the external API when running the integration tests.
How can I mock the response of the external API call?
Is there a way I can still send a POST request to my API (mocked or otherwise) within my test, but use a mocked response for the external call performed in the Java POST method?
I've done this as follows: create a service layer that makes the API calls to external services. I built mine on Spring's RestTemplate but you could use whatever library to make calls. So it'll have methods like get() or post().
Then I use Postman to perform some requests against the external API and save the responses in files and add these files to my project.
Finally, in my tests, I mock out the calls to my little API service layer so that instead of going to the external API, it reads from the test files I saved previously. This runs the code under test with known payloads that came from the external API, but without requiring a connection to it during the test, and which won't change until I update the responses in the files myself.
I use EasyMock but any mocking library will work. Here's an example of what a test looks like.
#Test
public void test_addPhoneToExternalService() {
// Mock my real API service.
ApiService mockApiService = EasyMock.createMock(ApiService.class);
// Construct my service under test using my mock instead of the real one.
ServiceUnderTest serviceUnderTest = new ServiceUnderTest(mockApiService);
// Expect the body of the POST request to look like this.
Map<String, Object> requestBody = new HashMap<String, Object>() {{
put("lists", 0);
put("phone", "800-555-1212");
put("firstName", "firstName");
put("lastName", "lastName");
}};
// Read the response that I manually saved as json.
JsonNode expectedResponse = Utils.readJson("response.json");
expect(mockApiService.post(anyObject(),
eq("https://rest.someservice.com/api/contacts"), eq(serialize(requestBody))))
.andReturn(expectedResponse);
EasyMock.replay(mockApiService);
// Call the code under test. It ingests the response
// provided by the API service, which is now mocked,
// and performs some operations, then returns a value
// based on what the response contained (for example,
// "{\"id\":42}").
long id = serviceUnderTest.addPhone("firstName", "lastName", "800-555-1212");
Assert.assertThat(id, is(42L));
EasyMock.verify(mockApiService);
}
Hope that helps!

Unit testing an API?

I have a class and a method which looks like this
class A {
private RetryLogic logic;
private Service serviceClient;
public A(){
logic = new RetryLogic();
serviceClient = new Service();
}
public Response methodA() {
Request request = new Request();
serviceClient.addRetryLogic(logic);
Responce response = serviceClient.call(request);
return response;
}
}
The retry logic in this case will retry the service call if there is a failure like Service not available or any HTTP error.
But it won't retry if the service is getting called and throwing any modeled exceptions.
If I am writing a unit test for methodA is it appropriate (or should I be concerned) to test the retry logic by adding a test which would mock an HTTP exception and check if the retry works ?
The RetryLogic is basically a different package not owned by me.
Would you test the setConnectionTimeout of the Apache Commons HttpClient? or the setAutoCommit of your JDBC Client? I wouldn't.
If you trust that Service as you trust the rest of your dependencies, it makes no sense to test it. Just imagine that there are 1000 developers using Service, and all of them are testing the retry functionality... it's a bit of waste of time, isn't it?

Unit testing a REST-Service

I have a REST-Service based on the spark-framework. Looks like this(simplyfied):
public void init() {
get(new Route("spark/favorites") {
#Override
public Object handle(Request request, Response response) {
ExternalService exS= new ExternalService();
ArrayList<String> favs= exS.getFavorites();
Gson gson = getGson();
return gson.toJson(favs);
}
});
}
Now I want to write some tests for my service to see if my get/post/put/delete does what I want.
Therefor I deploy it on an embedded Jetty during my tests.
The problem I a facing now is that my service depends on external REST-Services. I would like to mock all calls to those (to have a fast unit test). But I have no idea how to mock inside the running service.
Is that even possible? Should I switch to another REST-Framework?
Suggestions?
that's an integration test, no matter if your app talks to webservice mocks or real 3rd party webservices. Unit test are when you test your classes in isolation.
If you want to mock out external webservices you would need to make links to 3rd party apps configurable and have a separate configuration just for integration testing.
For webservice mocking you could use one of several available mocking frameworks:
https://sourceforge.net/projects/soaprest-mocker

How do I unit test code which calls the Jersey Client API?

I wrote code which calls the Jersey client API which in turn calls a web service which is out of my control. I do not want my unit test to call the actual web service.
What is the best approach for writing a unit test for code which calls the Jersey client API? Should I use the Jersey server API to write a JAX-RS web service and then use the Jersey Test Framework for the unit test? Or should I mock out the Jersey web service calls? I have access to JMock. Or should I try another approach?
During my research, I found this discussion describing various options, but I did find a complete solution. Are there any code examples available showing a suggested JUnit approach? I could not find any in the Jersey documentation.
Here is the relevant source code:
public String getResult(URI uri) throws Exception {
// error handling code removed for clarity
ClientConfig clientConfig = new DefaultClientConfig();
Client client = Client.create(clientConfig);
WebResource service = client.resource(uri);
String result = service.accept(accept).get(String.class);
return result;
}
Here are examples of test code I would like to pass. I would like to test (1) passing in a valid URI and getting a valid string back and (2) passing in an invalid (for whatever reason -- unreachable or unauthorized) URI and getting an exception back.
#Test
public void testGetResult_ValidUri() throws Exception {
String xml = retriever.getResult(VALID_URI);
Assert.assertFalse(StringUtils.isBlank(xml));
}
#Test(expected = IllegalArgumentException.class)
public void testGetResult_InvalidUri() throws Exception {
retriever.getResult(INVALID_URI);
}
Everything above is the simple description of what my code does. In reality, there is a layer on top of that that accepts two URIs, first tries calling the first URI, and if that URI fails then it tries calling the second URI. I would like to have unit tests covering (1) the first URI succeeds, (2) the first URI fails and the second URI succeeds, and (3) both URIs fail. This code is sufficiently complex that I want to test these different scenarios using JUnit, but to do this I either need to run actual stand-in web services or mock out the Jersey client API calls.
Try to use Mockito or Easymock for mocking service calls. You need to mock only these methods which are actually used - no need to mock every method. You can creat mock object for WebResource class, then mock accept method call.
In #BeforeClass/#Before JUnit test method write something like (Mockito example)
WebResource res = mock(WebResource.class);
when(res.accept(something)).thenReturn(thatWhatYouWant);
Then in your tests you can use res object as if it was real object and call mock method on it. Instead of returning value you can also throw exceptions. Mockito is pretty cool.
Typically what you are really after is "does the way I use the Jersey Client DSL produce a request to the correct URL with the correct payload and URL parameters". Testing this with Mockito is really verbose and the setup code will usually end up looking something like this:
when(authentication.queryParam(eq("sa"), anyBoolean())).thenReturn(testAuthentication);
when(testAuthentication.resolveTemplate("channel", "smf")).thenReturn(testAuthentication);
when(testAuthentication.request(
MediaType.APPLICATION_JSON_TYPE)).thenReturn(mockRequestBuilder);
when(mockRequestBuilder.post(any(Entity.class))).thenReturn(mockResponse);
when(mockResponse.readEntity(ResponseWrapper.class)).thenReturn(successfulAuthResponse());
And this is basically just for a single REST request. It's overly verbose, and instead of testing the hoped outcome you are just replicating the steps you think are correct in using the Jersey Client DSL.
Instead of the above, I would aim for mocking a simple service. For this I've used WireMock which starts a Jetty server and where I can stub things like "expect a request to this URL, respond with this message and verify that the payload is this".
I know this is edging on an integration test and it is a bit slower than just using Mockito but I value testing the real outcome and I value the readability of the tests way more in this case.
Setup for a WireMock based Jersey Client test looks something like this:
#Test
public void exactUrlOnly() {
stubFor(get(urlEqualTo("/some/thing"))
.willReturn(aResponse()
.withHeader("Content-Type", "text/plain")
.withBody("Hello world!")));
assertThat(testClient.get("/some/thing").statusCode(), is(200));
assertThat(testClient.get("/some/thing/else").statusCode(), is(404));
}
Just implement a work-alike service and in your unit test setup start the service using HttpServerFactory.

Categories

Resources