How to I unit test a #future method that uses a callout? - unit-testing

I have a trigger that fires when an opportunity is updated, as part of that I need to call our API with some detail from the opportunity.
As per many suggestions on the web I've created a class that contains a #future method to make the callout.
I'm trying to catch an exception that gets thrown in the #future method, but the test method isn't seeing it.
The class under test looks like this:
public with sharing class WDAPIInterface {
public WDAPIInterface() {
}
#future(callout=true) public static void send(String endpoint, String method, String body) {
HttpRequest req = new HttpRequest();
req.setEndpoint(endpoint);
req.setMethod(method);
req.setBody(body);
Http http = new Http();
HttpResponse response = http.send(req);
if(response.getStatusCode() != 201) {
System.debug('Unexpected response from web service, expecte response status status 201 but got ' + response.getStatusCode());
throw new WDAPIException('Unexpected response from web service, expecte response status status 201 but got ' + response.getStatusCode());
}
}
}
here's the unit test:
#isTest static void test_exception_is_thrown_on_unexpected_response() {
try {
WDHttpCalloutMock mockResponse = new WDHttpCalloutMock(500, 'Complete', '', null);
WDAPIInterface.send('https://example.com', 'POST', '{}');
} catch (WDAPIException ex) {
return;
}
System.assert(false, 'Expected WDAPIException to be thrown, but it wasnt');
}
Now, I've read that the way to test #future methods is to surround the call with Test.startTest() & Test.endTest(), however when I do that I get another error:
METHOD RESULT
test_exception_is_thrown_on_unexpected_response : Skip
MESSAGE
Methods defined as TestMethod do not support Web service callouts, test skipped
So the question is, how do I unit test a #future method that makes an callout?

The callout is getting skipped because the HttpCalloutMock isn't being used.
I assume that WDHttpCalloutMock implements HttpCalloutMock?
If so, a call to Test.setMock should have it return the mocked response to the callout.
WDHttpCalloutMock mockResponse = new WDHttpCalloutMock(500, 'Complete', '', null);
Test.setMock(HttpCalloutMock.class, mockResponse);
WDAPIInterface.send('https://example.com', 'POST', '{}');
Incidentally, the Salesforce StackExchange site is a great place to ask Salesforce specific questions.

Related

How to mock OkHttpClient request to external URL?

I have this code in my service:
public String requestValue() {
Call call = okHttpClient.newCall(new Request.Builder().url("external-url").build());
Response response = call.execute();
return response.body().string();
}
How can I mock the result of this call in a Junit test?
public void testRequestValue() {
// TODO mock http response
String result = myService.requestValue();
assertEquals("value", result);
}
note: naive solution with Mockito does not work. Mockito.eq does not trigger on Request objects (seems like Request.equals provides incorrect result for identical requests).
Request request = new Request.Builder().url("external-url").build();
Response response = new Response.Builder()
.request(request)
.protocol(Protocol.HTTP_2)
.code(200)
.message("")
.body(ResponseBody.create("value", MediaType.get("application/json")))
.build();
Call call = Mockito.mock(Call.class);
Mockito.when(call.execute()).thenReturn(response);
Mockito.when(okHttpClientMock.newCall(Mockito.eq(request))).thenReturn(call);
You could use wiremock or the MockServer provided by okhttp

SpringBootTest - Test exception when request is invalid

I developed an API using web-flux which is working fine when I make request using POSTMAN. My code is:
Controller:
#PostMapping("/post", produces = ["application/xml"])
fun post(#Valid request: RequestData): Mono<Response> {
return Mono.just(request)
...
...
...
}
dto:
data class RequestData(
#get:NotBlank
#get:Email
val email: String = "",
)
So whenever I pass invalid email via POSTMAN, I'm catching the exception like below and its working:
#ExceptionHandler
fun bindingExceptionHandler(e: WebExchangeBindException) = "Custom Error Message"
But now when I write UT(#WebFluxTest) for this case (Invalid emaid), It failed.
#Test
fun testWhenInvalidEmail() {
// Request body
val email = "invalidemail"
val request = LinkedMultiValueMap<String, String>()
request.add("email", email)
webTestClient.post().uri("/post")
.body(BodyInserters.fromFormData(request))
.exchange()
.expectStatus().isOk
}
When I debug this, I found that my exceptionHandler not coming into picture when request coming through unit test. I'm using application/x-www-form-urlencoded content type in POST request.
Please let me know where I'm doing wrong.
I followed this question as well but didn't work.
As mentioned on another related question, this has been fixed in Spring Boot 2.1.0.
Also, you shouldn't have to build WebTestClient yourself but instead inject it in your test class (see reference documentation about that):
#RunWith(SpringRunner.class)
#WebFluxTest(MyValidationController.class)
public class MyValidationControllerTests {
#Autowired
private WebTestClient webClient;
#Test
public void testWhenInvalidEmail() {
//...
}
}

What is the proper way to return http status code from service class in a Rest based java web application?

In restfull architecture , the service end points need to return http status code which can vary from 100 to 600. So i use Response to do this from Rest API class. But to send exact status code and message in response i make service class to return me response object with proper status code and message. Is this proper way ? Can service class return Response object (JAX-RS class)?
You can use javax.ws.rs.core.Response
Here is an example
#GET
#Path("cities")
public Response getCities() {
List<City> cities = null;
try{
cities = commonDelegate.getCities();
}
catch(Exception e)
{
return Response.status(Response.Status.NO_CONTENT).entity("no city found").build(); // return 204
}
return Response.ok().entity(cities).build(); return 200
}

Cannot call web api 2 post method with int parameter in URL in Unit Test using Http server

Please ignore the spelling mistake, I cannot copy code so I have typed the whole thing and changed name of controller and method.
WEB API 2
Controller:
// Controller name is Test
public HttpResponseMessage Method1(int param1) // Post method
{
// return string
}
If I create an object of controller in test case then it is working fine. But if I want to test in localhost using following code:
Unit Test:
public void Method1Test()
{
HttpResponseMessage response;
HttpConfiguration config = new HttpConfiguration();
config.Routes.MapHttpRoute("DefaultApi", "api/{controller}/{id}");
HttpServer server = new HttpServer(config);
using(var client = new HttpClient(server))
{
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "http://localhost:5022/api/test?param1=1");
request.Content = new ObjectContent<int>(param1, new JsonMediaTypeFormatter());
response = client.SendAsync(request, CancellationToken.None).Result;
};
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
}
Now, my test case is failing. I used the same code in different project and it worked. May be it is the way I am trying to call Post method. Is this the right way to call post method with Int parameter in URL?
In help page, under API column it shows:
POST api/test/param1={param1}
Also I have put some stop point in actual service I am cursor is not stopping at that point. Why?
If I want to call the same service from browser, what URL should I pass? Is it -
http://localhost:5022/api/test?param1=1
Or something else?
I figured it out. Following is the correct unit test method but this has some extra information which I have not provided earlier i.e., passing object as an input for the service.
private void Method1Test(ObjectClass obj)
{
HttpResponseMessage response = null;
HttpConfiguration config = new HttpConfiguration();
config.Routes.MapHttpRoute("DefaultApi", "api/{controller}/{id}");
HttpServer server = new HttpServer(config);
using (var client = new HttpClient(server))
{
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "http://localhost:5022/api/test/1");
request.Content = new ObjectContent<ObjectClass>(obj, new JsonMediaTypeFormatter());
response = client.SendAsync(request, CancellationToken.None).Result;
};
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
}
So the correct URL that I was looking for was
http://localhost:5022/api/test/1
Sorry, It took long to post this answer. This method is working like a charm for more then 2 years.

Resteasy, Make a Http-Put or Http-Post with Server-side Mock Framework of RestEasy

I wrote a Rest-Service which i would like to test.
I wanna run a JUnit test without having my server run. For this I am using the Server-side Mock Framework of RestEasy.
My question is, how can I make a Http-Put or Http-Post request with this framework with an marshalled Java Object in the Http-Body???
The Code below runs fine for an Http-Get, but how to make a Put or Post, maybe someone got some example code for this???
#Test
public void testClient() throws Exception {
Dispatcher dispatcher = MockDispatcherFactory.createDispatcher();
POJOResourceFactory noDefaults = new POJOResourceFactory(
MyClass.class);
dispatcher.getRegistry().addResourceFactory(noDefaults);
{
MockHttpRequest request = MockHttpRequest.get("/message/test/"
+ requestParam);
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
}
}
A bit late response but , might have some use for someone.
This is how i usually test my PUT requests. Hope it helps
#Test
public void testPUT() throws Exception {
POJOResourceFactory noDefaults = new POJOResourceFactory(REST.class);
Dispatcher dispatcher = MockDispatcherFactory.createDispatcher();
dispatcher.getRegistry().addResourceFactory(noDefaults);
String url = "your_url_here";
MockHttpRequest request = MockHttpRequest.put(url);
request.accept(MediaType.APPLICATION_JSON);
request.contentType(MediaType.APPLICATION_JSON_TYPE);
// res being your resource object
request.content(res.toJSONString().getBytes());
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
Assert.assertEquals( HttpStatus.SC_CREATED,response.getStatus());
}