I'm struggling to create a simple stub. I've been following the wiremock tutorial online but have had no success on the issue im facing. Particularly, the "Get" method in the AssertMethod is giving me a lot of issues. I don't know how to resolve it.
public class WeatherApplicationTest {
#Rule
public WireMockRule wireMockRule = new WireMockRule();
public WireMockServer wireMockServer = new WireMockServer(); //No-args constructor will start on port 8080, no HTTPS
#BeforeClass
public void setUpClass() {
wireMockServer.start();
}
#AfterClass
public void tearDownClass() {
wireMockServer.stop();
}
#Test
public void statusMessage() throws IOException{
wireMockRule.stubFor(get(urlEqualTo("/some/thing"))
.willReturn(aResponse()
.withStatus(200)
.withStatusMessage("Everything is fine")
.withHeader("Content-Type", "Text/Plain")));
HttpClient client = new DefaultHttpClient();
HttpGet request = new HttpGet("http://localhost:" + wireMockServer.port() + "/some/thing");
HttpResponse response = client.execute(request);
assertThat(response.GET("/some/thing").statusCode(), is(200));
}
}
I'm getting an error on the following line:
assertThat(response.GET("/some/thing").statusCode(), is(200));
The GET method is underlined in Red.
Please Help!
The WireMock documentation doesn't do a great job of outlining how to connect with it (but, in their defense, it's not really in their slice of the pie).
As I eluded to in the comments, HttpResponse does not contain a GET() method, which is why you're getting the "red underline" (IE: error).
So we know we are looking to make an assertion against the status code. If we look at the Javadoc of the HttpResponse class, there is the StatusLine class that can be retrieved from getStatusLine() of HttpResponse, and the Javadoc of that class shows that it contains a getStatusCode() method. Combining this information into your answer, the assertion needs to be updated to:
assertThat(response.getStatusLine().getStatusCode(), is(200));
In addition, as pointed out by Tom in the comments, remove the WireMockServer (as well as the start/stop and getPort() calls; you can get the port from your rule. And the stubFor(...) method should be called statically (not as a part of the rule).
My contribution to this question: this Java test runs without exceptions, both in the IDE and in Maven as well, and, moreover with the other tests, because at the end, it shuts down the mock server, and doesn't make any conflicts with other servers running in the other tests.
import com.github.tomakehurst.wiremock.WireMockServer;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import wiremock.org.apache.http.HttpResponse;
import wiremock.org.apache.http.client.HttpClient;
import wiremock.org.apache.http.client.methods.HttpGet;
import wiremock.org.apache.http.impl.client.DefaultHttpClient;
import java.io.IOException;
import static com.github.tomakehurst.wiremock.client.WireMock.*;
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class KatharsisControllerTest {
private static WireMockServer mockedServer;
private static HttpClient client;
#BeforeClass
public static void setUpClass() {
mockedServer = new WireMockServer();
mockedServer.start();
client = new DefaultHttpClient();
}
#AfterClass
public static void tearDown() {
mockedServer.stop();
}
#Test
public void test_get_all_mock() {
stubFor(get(urlEqualTo("/api/Payment"))
.willReturn(aResponse()
.withHeader("Content-Type", "application/vnd.api+json;charset=UTF-8")
.withStatus(200)
.withBody("")
));
HttpGet request = new HttpGet("http://localhost:8080/api/Payment");
try {
HttpResponse response = client.execute(request);
assert response.getStatusLine().getStatusCode() == 200;
} catch (IOException e) {
e.printStackTrace();
}
}
}
Related
My gateway will redirect traffic to many different services (under different domain names). how can i test the gateway's configuration? with only one service i can just setup the mock server (like httpbin) and test the response. with multiple services i'd prefer to avoid starting the whole docker network or changing the locak dns aliases. does spring offer any lightweight way of testing the gateway?
Here is how to achieve what you want with the API Simulator:
package my.package;
import static com.apisimulator.embedded.SuchThat.isEqualTo;
import static com.apisimulator.embedded.SuchThat.startsWith;
import static com.apisimulator.embedded.http.HttpApiSimulation.httpApiSimulation;
import static com.apisimulator.embedded.http.HttpApiSimulation.httpRequest;
import static com.apisimulator.embedded.http.HttpApiSimulation.httpResponse;
import static com.apisimulator.embedded.http.HttpApiSimulation.simlet;
import static com.apisimulator.http.Http1Header.CONTENT_TYPE;
import static com.apisimulator.http.HttpMethod.CONNECT;
import static com.apisimulator.http.HttpMethod.GET;
import static com.apisimulator.http.HttpStatus.OK;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
import java.time.Duration;
import java.util.Map;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.util.SocketUtils;
import com.apisimulator.embedded.http.JUnitHttpApiSimulation;
#RunWith(SpringRunner.class)
#SpringBootTest(
webEnvironment = RANDOM_PORT,
properties = {
"management.server.port=${test.port}", "logging.level.root=info",
// Configure the Gateway to use HTTP proxy - the API Simulator
// instance running at localhost:6090
"spring.cloud.gateway.httpclient.proxy.host=localhost",
"spring.cloud.gateway.httpclient.proxy.port=6090"
//"logging.level.reactor.netty.http.server=debug",
//"spring.cloud.gateway.httpserver.wiretap=true"
}
)
#Import(ServiceGatewayApplication.class)
public class ServiceGatewayApplicationTest
{
// Configure an API simulation. This starts up an instance
// of API Simulator on localhost, default port 6090
#ClassRule
public static final JUnitHttpApiSimulation clApiSimulation = JUnitHttpApiSimulation
.as(httpApiSimulation("svc-gateway-backends"));
protected static int managementPort;
#LocalServerPort
protected int port = 0;
protected String baseUri;
protected WebTestClient webClient;
#BeforeClass
public static void beforeClass()
{
managementPort = SocketUtils.findAvailableTcpPort();
System.setProperty("test.port", String.valueOf(managementPort));
// Configure simlets for the API simulation
// #formatter:off
clApiSimulation.add(simlet("http-proxy")
.when(httpRequest(CONNECT))
.then(httpResponse(OK))
);
clApiSimulation.add(simlet("test-domain-1")
.when(httpRequest()
.whereMethod(GET)
.whereUriPath(isEqualTo("/static"))
// The `host` header is used to determine the actual destination
.whereHeader("host", startsWith("domain-1.com"))
)
.then(httpResponse()
.withStatus(OK)
.withHeader(CONTENT_TYPE, "application/text")
.withBody("{ \"domain\": \"1\" }")
)
);
clApiSimulation.add(simlet("test-domain-2")
.when(httpRequest()
.whereMethod(GET)
.whereUriPath(isEqualTo("/v1/api/foo"))
.whereHeader("host", startsWith("domain-2.com"))
)
.then(httpResponse()
.withStatus(OK)
.withHeader(CONTENT_TYPE, "application/json; charset=UTF-8")
.withBody(
"{\n" +
" \"domain\": \"2\"\n" +
"}"
)
)
);
// #formatter:on
}
#AfterClass
public static void afterClass()
{
System.clearProperty("test.port");
}
#Before
public void setup()
{
// #formatter:off
baseUri = "http://localhost:" + port;
webClient = WebTestClient.bindToServer()
.baseUrl(baseUri)
.responseTimeout(Duration.ofSeconds(2))
.build();
// #formatter:on
}
#Test
public void test_domain1()
{
// #formatter:off
webClient.get()
.uri("/static")
.exchange()
.expectStatus().isOk()
.expectBody(String.class).consumeWith(result ->
assertThat(result.getResponseBody()).isEqualTo("{ \"domain\": \"1\" }")
);
// #formatter:on
}
#Test
public void test_domain2()
{
// #formatter:off
webClient.get()
.uri("/v1/api/foo")
.exchange()
.expectStatus().isOk()
.expectHeader()
.contentType("application/json; charset=UTF-8")
.expectBody(Map.class).consumeWith(result ->
assertThat(result.getResponseBody()).containsEntry("domain", "2")
);
// #formatter:on
}
}
Most of the code is based on this GatewaySampleApplicationTests class from the Spring Cloud Gateway project.
The above assumes the Gateway has routes similar to these (snippets only):
...
uri: "http://domain-1.com"
predicates:
- Path=/static
...
uri: "http://domain-2.com"
predicates:
- Path=/v1/api/foo
...
#apsisim provided a great idea to use web proxy. but the tool he suggests is not in any maven repo and has commercial license. what worked for me:
run the gateway so it will use a proxy (u can be more fancy and find a free port):
private const val proxyPort = 1080
#SpringBootTest(
properties = [
//"logging.level.reactor.netty.http.server=debug",
//"spring.cloud.gateway.httpserver.wiretap=true",
//"spring.cloud.gateway.httpclient.wiretap=true",
"spring.cloud.gateway.httpclient.proxy.host=localhost",
"spring.cloud.gateway.httpclient.proxy.port=$proxyPort"
]
)
then use the mockwebserver as a proxy
testImplementation("com.squareup.okhttp3:mockwebserver:4.2.1")
testImplementation("com.squareup.okhttp3:okhttp:4.2.1")
and then all your requests will go to your proxy. just remember that http protocol specifies that first request to new server requires tunneling via the proxy so when u do first request to the gateway, the gateway will send 2 requests to the proxy:
testClient.get()
.uri(path)
.header("host", gatewayDns)
.exchange()
nextRequestFromGateway {
method `should equal` "CONNECT"
headers[HOST] `should equal` "$realUrlBehindGateway:80"
}
nextRequestFromGateway {
path `should equal` "/api/v1/whatever"
headers[HOST] `should equal` realUrlBehindGateway
}
...
fun nextRequestFromGateway(block : RecordedRequest.() -> Unit) {
mockWebServer.takeRequest().apply (block)
}
I am using the following code for making the service call using the Rest assured library, can you help me to resolve the build issues. There is a problem in finding the function "equalTo" to validate the response data.
See error below:
Here is my code:
#Test
public void testFileUpload() {
final File file = new File(getClass().getClassLoader()
.getResource("test.txt").getFile());
assertNotNull(file);
assertTrue(file.canRead());
given()
.multiPart(file)
.expect()
// problem here
.body(equalsTo("This is an uploaded test file."))
.when()
.post("/service/file/upload");
}
#Test
public void testGetSingleUser() {
given()
.expect()
.statusCode(200)
.body(
"email", equalTo("test#hascode.com"),
"firstName", equalTo("Tim"),
"lastName", equalTo("Testerman"),
"id", equalTo("1"))
.when()
.`enter code here`
.get("/service/single-user");
}
I assume it is the equalTo from the hamcrest library.
Can you add this import and see if it works
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.equalTo;
With adding above hamcrest library, Error will be resolved.
I have tried and it worked.
Import this Line you will get the equalTo()
import static org.hamcrest.Matchers.*;
I have set the keep alive timeout in spring boot embeded tomcat server to 30 seconds. So i use below in the Application.java,
#Bean
public EmbeddedServletContainerFactory getEmbeddedServletContainerFactory() {
TomcatEmbeddedServletContainerFactory containerFactory = new TomcatEmbeddedServletContainerFactory();
containerFactory
.addConnectorCustomizers(new TomcatConnectorCustomizer() {
#Override
public void customize(Connector connector) {
((AbstractProtocol) connector.getProtocolHandler())
.setKeepAliveTimeout(30000);
}
});
return containerFactory;
}
Then i sleep a request thread for 40 seconds from my rest controller. But when i make a request via postman it successfully return HTTP status code 200 instead it should return gateway timeout error.
I try both setConnectionTimeout and setKeepAliveTimeout and it did not work.
What am i missing here?
Edit question: My initial problem
Let me explain the original question of mine, which lead me to ask above question.
Well i have a long poll process which normally runs about more than 5 minits.
So what happen is when i call the Rest API for longpoll, After 2.2 minits i get a 504 http error in browser.
I am using a AWS environment, where i have a ELB and a HAProxy which is installed in AWS EC2 instance.
As per AWS doc, it says the default Idle Connection Timeout of ELB is 60 seconds. So i have increase it to up to 30 mins.
Moreover it says,
If you use HTTP and HTTPS listeners, we recommend that you enable the
keep-alive option for your EC2 instances. You can enable keep-alive in
your web server settings or in the kernel settings for your EC2
instances.
So have increase the embedded tomcat keep-alive timeout like above code snippet to 30.2 mins
So now i expect my long poll request to be completed, with out getting a 504 error. But still i get 504 error in browser?
Ref: AWS dev guide
It looks like you want to close abandoned HTTP connections which might occur on mobile devices.
#RestController
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#Bean
public EmbeddedServletContainerFactory getEmbeddedServletContainerFactory() {
TomcatEmbeddedServletContainerFactory containerFactory = new TomcatEmbeddedServletContainerFactory();
containerFactory
.addConnectorCustomizers(new TomcatConnectorCustomizer() {
#Override
public void customize(Connector connector) {
((AbstractProtocol) connector.getProtocolHandler()).setConnectionTimeout(100);
}
});
return containerFactory;
}
#RequestMapping
public String echo(#RequestBody String body) {
return body;
}
}
Connection timeout has been set to 100 millisencods in order to run my tests fast. Data is sent in chunks. Between every chunk the running thread is suspended for x milliseconds.
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = DemoApplication.class)
#WebIntegrationTest("server.port:19000")
public class DemoApplicationTests {
private static final int CHUNK_SIZE = 1;
private static final String HOST = "http://localhost:19000/echo";
#Rule
public ExpectedException expectedException = ExpectedException.none();
#Test
public void slowConnection() throws Exception {
final HttpURLConnection connection = openChunkedConnection();
OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream());
writeAndWait(500, out, "chunk1");
writeAndWait(1, out, "chunk2");
out.close();
expectedException.expect(IOException.class);
expectedException.expectMessage("Server returned HTTP response code: 400 for URL: " + HOST);
assertResponse("chunk1chunk2=", connection);
}
#Test
public void fastConnection() throws Exception {
final HttpURLConnection connection = openChunkedConnection();
OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream());
writeAndWait(1, out, "chunk1");
writeAndWait(1, out, "chunk2");
out.close();
assertResponse("chunk1chunk2=", connection);
}
private void assertResponse(String expected, HttpURLConnection connection) throws IOException {
Scanner scanner = new Scanner(connection.getInputStream()).useDelimiter("\\A");
Assert.assertEquals(expected, scanner.next());
}
private void writeAndWait(int millis, OutputStreamWriter out, String body) throws IOException, InterruptedException {
out.write(body);
Thread.sleep(millis);
}
private HttpURLConnection openChunkedConnection() throws IOException {
final URL url = new URL(HOST);
final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoOutput(true);
connection.setChunkedStreamingMode(CHUNK_SIZE);
return connection;
}
}
Set log level for package org.apache.catalina.core to DEBUG
logging.level.org.apache.catalina.core=DEBUG
and you can see a SocketTimeoutException for slowConnection test.
I don't know why you want HTTP status code 502 as error response status. HTTP 502 says:
The 502 (Bad Gateway) status code indicates that the server, while
acting as a gateway or proxy, received an invalid response from an
inbound server it accessed while attempting to fulfill the request.
The client Postman calls your server application. I don't see any gateway or proxy in between.
If you just condensed your question to a bare minimum and in reality you want to build a proxy on your own, you might consider using Netflix Zuul.
Update 23.03.2016:
That is the root cause for OP's question on Stackoverflow:
What i did with longpolling was, from service api, i sleep the thread for some time and wake it and do it again and again untill some db status is completed.
That implementation actually prevents the Tomcat worker thread from processing new HTTP requests. As a result your request throughput reduces with every additional long running operation.
I propose to offload the long running operation into a separate thread. The client (browser) initiates a new request to fetch the result.
Depending on the processing status, server returns either the result or a notification/error/warning/.
Here's a very simple example :
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import static org.springframework.http.HttpStatus.CREATED;
import static org.springframework.http.HttpStatus.NOT_FOUND;
import static org.springframework.http.HttpStatus.OK;
#RestController
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
private ExecutorService executorService = Executors.newFixedThreadPool(10);
private Map<String, String> results = new ConcurrentHashMap<>();
#RequestMapping(path = "put/{key}", method = RequestMethod.POST)
public ResponseEntity<Void> put(#PathVariable String key) {
executorService.submit(() -> {
try {
//simulate a long running process
Thread.sleep(10000);
results.put(key, "success");
} catch (InterruptedException e) {
results.put(key, "error " + e.getMessage());
Thread.currentThread().interrupt();
}
});
return new ResponseEntity<>(CREATED);
}
#RequestMapping(path = "get/{key}", method = RequestMethod.GET)
public ResponseEntity<String> get(#PathVariable String key) {
final String result = results.get(key);
return new ResponseEntity<>(result, result == null ? NOT_FOUND : OK);
}
}
I'm trying to set the User-Agent with React Native on Android. Did some research and it looks like I should use an okhttp Interceptor. An example that I've found explains how this should be done(Link) but then I am not sure on how to register the Interceptor.
So in order to set the User-Agent I am using this class:
public class CustomInterceptor implements Interceptor {
#Override public Response intercept(Interceptor.Chain chain) throws IOException {
Request originalRequest = chain.request();
Request requestWithUserAgent = originalRequest.newBuilder()
.removeHeader("User-Agent")
.header("User-Agent", "Trevor")
.build();
return chain.proceed(requestWithUserAgent);
}
}
Then what's left is to register the above interceptor so where it should be done? Maybe in MainActivity.java?
OkHttpClient okHttp = new OkHttpClient();
okHttp.interceptors().add(new CustomInterceptor());
I am not getting any errors when building the app so I think that the CustomInterceptor should be fine - just need to make the app use it.
UPDATE:
I'm currently trying to register the interceptor in MainActivity but it won't pick it up:
public class MainActivity extends ReactActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
OkHttpClient client = new OkHttpClient();
client.networkInterceptors().add(new CustomInterceptor());
};
};
None of the answers here worked for me for RN 0.63.2. I was able to get it working and in my research was able to find the (albeit very scarce) documentation for the support of this feature.
The only documentation I could find for this was this PR where someone added support for this feature (and broke the currently accepted answer). When I tried adding the interceptor as documented in the PR I got an exception related to CookieJar which I was able to find a solution to in this (unresolved 🙄) issue.
TLDR:
Add a Java class in the same folder as your MainApplication called UserAgentInterceptor.java and place this in it:
package YOUR.PACKAGE.NAME; // <-- REPLACE ME
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
import java.io.IOException;
public class UserAgentInterceptor implements Interceptor {
public UserAgentInterceptor() {}
#Override
public Response intercept(Interceptor.Chain chain) throws IOException {
Request originalRequest = chain.request();
Request requestWithUserAgent = originalRequest.newBuilder()
.removeHeader("User-Agent")
.addHeader("User-Agent", "YOUR USER AGENT") // <-- REPLACE ME
.build();
return chain.proceed(requestWithUserAgent);
}
}
Then create another Java class in the same folder named UserAgentClientFactory.java and place this in it:
package YOUR.PACKAGE.NAME; // <-- REPLACE ME
import com.facebook.react.modules.network.OkHttpClientFactory;
import com.facebook.react.modules.network.ReactCookieJarContainer;
import okhttp3.OkHttpClient;
public class UserAgentClientFactory implements OkHttpClientFactory {
public OkHttpClient createNewNetworkModuleClient() {
return new OkHttpClient.Builder()
.cookieJar(new ReactCookieJarContainer())
.addInterceptor(new UserAgentInterceptor())
.build();
}
}
Then in your MainApplication onCreate method register the factory like this:
...
import com.facebook.react.modules.network.OkHttpClientProvider;
...
#Override
public void onCreate() {
super.onCreate();
OkHttpClientProvider.setOkHttpClientFactory(new UserAgentClientFactory());
// Your other code stuffs
}
And that's it!
So I've finally figured it out. Here is the solution for overriding the User-Agent of okhttp3 with React Native.
Create a file called CustomInterceptor.java:
package com.trevor;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
import java.io.IOException;
public class CustomInterceptor implements Interceptor {
public CustomInterceptor() {}
#Override
public Response intercept(Interceptor.Chain chain) throws IOException {
Request originalRequest = chain.request();
Request requestWithUserAgent = originalRequest.newBuilder()
.removeHeader("User-Agent")
.addHeader("User-Agent", "Trevor")
.build();
return chain.proceed(requestWithUserAgent);
}
}
Then in MainActivity.java override the onCreate method:
...
import com.facebook.react.modules.network.OkHttpClientProvider;
...
public class MainActivity extends ReactActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
attachInterceptor();
}
private void attachInterceptor() {
OkHttpClient client = OkHttpClientProvider.getOkHttpClient();
client.networkInterceptors().add(new CustomInterceptor());
}
}
Note that I'm importing com.facebook.react.modules.network.OkHttpClientProvider; and overriding that client instead of creating a vanilla OkHttpClient since this is the one that React Native will use.
React Native is iterating so quickly that the accepted answer didn't work for me.
For RN 0.27.2 I had to import okhttp3.OkHttpClient in my CustomInterceptor and change the attachInterceptor() method in MainActivity to replace the client.
private void attachInterceptor() {
OkHttpClient currentClient = OkHttpClientProvider.getOkHttpClient();
OkHttpClient replacementClient = currentClient.newBuilder().addNetworkInterceptor(new CustomInterceptor()).build();
OkHttpClientProvider.replaceOkHttpClient(replacementClient);
}
Everything else from ekonstantinidis's answer works for me.
Old issue, but we still ran into the same problem with React Native 0.59. This is what we did to fix (in Kotlin), as recent versions of okhttp prevent (and throw an exception) when trying to add an interceptor to an already initialized client:
import android.os.Build
import com.facebook.react.modules.network.OkHttpClientFactory
import com.jaredrummler.android.device.DeviceName
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.Response
class UserAgentInterceptor(val userAgent: String): Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val originalRequest = chain.request()
val correctRequest = originalRequest.newBuilder()
.removeHeader("User-Agent")
.addHeader("User-Agent", userAgent)
.build()
return chain.proceed(correctRequest)
}
}
class UserAgentClientFactory(val appName: String, val appVersion: String, val buildNumber: String): OkHttpClientFactory {
private fun userAgentValue(): String {
val deviceName = DeviceName.getDeviceName()
val osVersion = Build.VERSION.RELEASE
return "$appName/$appVersion (build: $buildNumber; device: $deviceName; OS: Android $osVersion)"
}
override fun createNewNetworkModuleClient(): OkHttpClient {
val builder = com.facebook.react.modules.network.OkHttpClientProvider.createClientBuilder()
return builder.addInterceptor(UserAgentInterceptor(userAgent = userAgentValue())).build()
}
}
This was done in a shared library between 2 apps, thus why we passed in the app name, version, and build number.
Usage from the app itself looked like:
private fun configureUserAgent() {
val versionName = BuildConfig.VERSION_NAME
val versionCode = BuildConfig.VERSION_CODE
OkHttpClientProvider.setOkHttpClientFactory(UserAgentClientFactory(appName = "My App", appVersion = versionName, buildNumber = "$versionCode"))
}
This was called from the onCreate method in the main activity of the app.
Hope this helps!
I've implemented this functionality using OkHttp and my code is pretty the same as yours - and everything works fine.
Consider using addHeader("User-Agent", "Trevor") instead of header("User-Agent", "Trevor"), because the latter will replace all of already set headers.
I'm using okHttp.networkInterceptors().add(new CustomInterceptor()); instead of okHttp.interceptors().add(new CustomInterceptor());, but I don't think it's a matter of concern here.
Update I do it in onCreate() method too. Everything works as it should.
This is my method inside my controller which is annotated by #Controller
#RequestMapping(value = "/getServerAlertFilters/{serverName}/", produces = "application/json; charset=utf-8")
#ResponseBody
public JSONObject getServerAlertFilters(#PathVariable String serverName) {
JSONObject json = new JSONObject();
List<FilterVO> filteredAlerts = alertFilterService.getAlertFilters(serverName, "");
JSONArray jsonArray = new JSONArray();
jsonArray.addAll(filteredAlerts);
json.put(SelfServiceConstants.DATA, jsonArray);
return json;
}
I am expecting {"data":[{"useRegEx":"false","hosts":"v2v2v2"}]} as my json.
And this is my JUnit test:
#Test
public final void testAlertFilterView() {
try {
MvcResult result = this.mockMvc.perform(get("/getServerAlertFilters/v2v2v2/").session(session)
.accept("application/json"))
.andDo(print()).andReturn();
String content = result.getResponse().getContentAsString();
LOG.info(content);
} catch (Exception e) {
e.printStackTrace();
}
}
Here is the console output:
MockHttpServletResponse:
Status = 406
Error message = null
Headers = {}
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
Even result.getResponse().getContentAsString() is an empty string.
Can someone please suggest how to get my JSON in my JUnit test method so that I can complete my test case.
I use TestNG for my unit testing. But in Spring Test Framework they both looks similar. So I believe your test be like below
#Test
public void testAlertFilterView() throws Exception {
this.mockMvc.perform(get("/getServerAlertFilters/v2v2v2/").
.andExpect(status().isOk())
.andExpect(content().json("{'data':[{'useRegEx':'false','hosts':'v2v2v2'}]}"));
}
If you want check check json Key and value you can use jsonpath
.andExpect(jsonPath("$.yourKeyValue", is("WhatYouExpect")));
You might find thatcontent().json() are not solveble please add
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
The 406 Not Acceptable status code means that Spring couldn't convert the object to json. You can either make your controller method return a String and do return json.toString(); or configure your own HandlerMethodReturnValueHandler. Check this similar question Returning JsonObject using #ResponseBody in SpringMVC
You can try the below for get and post methods
#Autowired
private MuffinRepository muffinRepository;
#Test
public void testGetMethod throws Exception(){
Muffin muffin = new Muffin("Butterscotch");
muffin.setId(1L);
BddMockito.given(muffinRepository.findOne(1L)).
willReturn(muffin);
mockMvc.perform(MockMvcRequestBuilders.
get("/muffins/1")).
andExpect(MockMvcResutMatchers.status().isOk()).
andExpect(MockMvcResutMatchers.content().string("{\"id\":1, "flavor":"Butterscotch"}"));
}
//Test to do post operation
#Test
public void testPostMethod throws Exception(){
Muffin muffin = new Muffin("Butterscotch");
muffin.setId(1L);
BddMockito.given(muffinRepository.findOne(1L)).
willReturn(muffin);
mockMvc.perform(MockMvcRequestBuilders.
post("/muffins")
.content(convertObjectToJsonString(muffin))
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andExpect(MockMvcResutMatchers.status().isCreated())
.andExpect(MockMvcResutMatchers.content().json(convertObjectToJsonString(muffin)));
}
If the response is empty then make sure to override equals() and hashCode() methods on the Entity your repository is working with:
//Converts Object to Json String
private String convertObjectToJsonString(Muffin muffin) throws JsonProcessingException{
ObjectWriter writer = new ObjectWriter().writer().withDefaultPrettyPrinter();
return writer.writeValueAsString(muffin);
}
There are 2 ways to check JSON responses. Lemme guide you through both of them, (taking test method from the question above, and assuming response {"data":[{"useRegEx":"false","hosts":"v2v2v2"}]} as given above)
Method 1) Asserting complete JSON
#Test
public final void testAlertFilterView() {
mockMvc.perform(get("/getServerAlertFilters/v2v2v2/")
.contentType("application/json"))
.andExpect(status().isOk())
// you may even read bigger json responses from file and convert it to string, instead of simply hardcoding it in test class
.andExpect(content().json("{"data":[{"useRegEx":"false","hosts":"v2v2v2"}]}"))
}
Method 2) Asserting specific key-value of response (not writing redundant piece of code)
.andExpect(jsonPath("$.data[0].useRegEx").value(false))
.andExpect(jsonPath("$.data[0].hosts").value("v2v2v2"));
Another thing you might need is the import statement,
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
If you want to check a few values in a specific field of JSON
.andExpect(MockMvcResultMatchers.jsonPath("$.message",
AllOf.allOf(
StringContains.containsString("name: must not be null"),
StringContains.containsString("type: must not be null")
)));
How it looks in the test class. JUnit4.
import com.fasterxml.jackson.databind.ObjectMapper;
import org.hamcrest.core.AllOf;
import org.hamcrest.core.StringContains;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.data.web.PageableHandlerMethodArgumentResolver;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
#RunWith(MockitoJUnitRunner.class)
public class YourControllerTest {
#Mock
private YourService service;
private MockMvc mvc;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mvc = MockMvcBuilders
.standaloneSetup(new YourController(service))
.setControllerAdvice(new YourExceptionHandler())
.setCustomArgumentResolvers(new PageableHandlerMethodArgumentResolver())
.build();
}
#Test
public void yourControllerMethodName_400_validation() throws Exception {
String path = "/orders/{orderId}/items";
Integer orderId = 123;
YourRequestDto requestDto = YourTestFactory.buildYourRequestDto();
requestDto.setName(null);
requestDto.setType(null);
YourResponseDto expected = YourTestFactory.buildYourResponseDto(requestDto);
Mockito
.when(service.someMethod(orderId, requestDto))
.thenReturn(expected);
mvc
.perform(
MockMvcRequestBuilders.post(path, orderId)
.contentType(MediaType.APPLICATION_JSON)
.content(new ObjectMapper().writeValueAsString(requestDto))
)
.andExpect(MockMvcResultMatchers.status().isBadRequest())
.andExpect(MockMvcResultMatchers.jsonPath("$.message",
AllOf.allOf(
StringContains.containsString("name: must not be null"),
StringContains.containsString("type: must not be null")
)));
}
}