Brion VIBBER has uploaded a new change for review.
https://gerrit.wikimedia.org/r/93528
Change subject: Break out mockable ApiHttpRequest & ApiHttpRequestFactory
interfaces
......................................................................
Break out mockable ApiHttpRequest & ApiHttpRequestFactory interfaces
This separates the HttpRequest library class out with an intermediate
interface (ApiHttpRequest and ApiHttpRequestFactory) which can be used
to either use mock objects for unit tests or swap in a new HTTP backend
class in future.
Api objects get initialized with an ApiHttpRequestFactory, which they'll
use to actually perform network requests.
Adds tests for ApiResult and a little for ApiRequest.
Change-Id: I038f0acb7c8dc3f61980bff7999acd641b801523
---
M src/main/java/org/mediawiki/api/json/Api.java
A src/main/java/org/mediawiki/api/json/ApiHttpRequest.java
A src/main/java/org/mediawiki/api/json/ApiHttpRequestException.java
A src/main/java/org/mediawiki/api/json/ApiHttpRequestFactory.java
M src/main/java/org/mediawiki/api/json/ApiRequest.java
A src/main/java/org/mediawiki/api/json/RealApiHttpRequest.java
A src/main/java/org/mediawiki/api/json/RealApiHttpRequestFactory.java
M src/main/java/org/mediawiki/api/json/RequestBuilder.java
M src/test/java/org/mediawiki/api/json/ApiConstructionTest.java
A src/test/java/org/mediawiki/api/json/ApiRequestTest.java
M src/test/java/org/mediawiki/api/json/ApiTest.java
A src/test/java/org/mediawiki/api/json/MockApiHttpRequest.java
A src/test/java/org/mediawiki/api/json/MockApiHttpRequestFactory.java
13 files changed, 184 insertions(+), 38 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/apps/android/java-mwapi
refs/changes/28/93528/1
diff --git a/src/main/java/org/mediawiki/api/json/Api.java
b/src/main/java/org/mediawiki/api/json/Api.java
index 23633b1..108e90b 100644
--- a/src/main/java/org/mediawiki/api/json/Api.java
+++ b/src/main/java/org/mediawiki/api/json/Api.java
@@ -1,7 +1,5 @@
package org.mediawiki.api.json;
-import com.github.kevinsawicki.http.HttpRequest;
-
import java.net.MalformedURLException;
import java.net.URL;
@@ -12,13 +10,17 @@
* - Uses JSON for everything
*/
public class Api {
+ /**
+ * Factory for setting up HTTP requests. May be replaced with a mock
object for testing.
+ */
+ public ApiHttpRequestFactory httpRequestFactory;
/**
- * Parameter to {@link #makeRequest(int, RequestBuilder)}, performs GET
request.
+ * Parameter to {@link #setupRequest(int, RequestBuilder)}, performs GET
request.
*/
public static final int METHOD_GET = 1;
/**
- * Parameter to {@link #makeRequest(int, RequestBuilder)}, performs POST
request.
+ * Parameter to {@link #setupRequest(int, RequestBuilder)}, performs POST
request.
*/
public static final int METHOD_POST = 2;
@@ -35,28 +37,32 @@
*
* @param domain Domain name of the server with the wiki to connect to
*/
- public Api(final String domain) {
- this(domain, true);
+ public Api(final ApiHttpRequestFactory httpRequestFactory, final String
domain) {
+ this(httpRequestFactory, domain, true);
}
/**
* Create an Api class with the default endpoint path.
*
+ * @param httpRequestFactory dependency to prep HTTP requests
* @param domain Domain name of the mediawiki installation to connect to
* @param useSecure true to use https, false to use http
*/
- public Api(final String domain, final boolean useSecure) {
- this(domain, useSecure, "/w/api.php");
+ public Api(final ApiHttpRequestFactory httpRequestFactory, final String
domain, final boolean useSecure) {
+ this(httpRequestFactory, domain, useSecure, "/w/api.php");
}
/**
* Create an Api class with given hostname and endpoint path.
*
+ * @param httpRequestFactory dependency to prep HTTP requests
* @param domain Domain name of the mediawiki installation to connect to
* @param useSecure true to use https, false to use http
* @param endpointPath Path to the json.php file. Require preceding slash.
*/
- public Api(final String domain, final boolean useSecure, final String
endpointPath) {
+ public Api(final ApiHttpRequestFactory httpRequestFactory, final String
domain, final boolean useSecure, final String endpointPath) {
+ this.httpRequestFactory = httpRequestFactory;
+
String protocol;
if (useSecure) {
protocol = "https";
@@ -106,14 +112,14 @@
* @param requestBuilder The requestBuilder to use to construct the request
* @return An {@link ApiRequest} object which can be used to get the
result of this query or cancel it.
*/
- public ApiRequest setupRequest(final int method, final RequestBuilder
requestBuilder) {
- HttpRequest request;
+ public ApiRequest setupRequest(final int method, final RequestBuilder
requestBuilder) throws ApiException {
+ ApiHttpRequest request;
switch(method) {
case METHOD_GET:
- request = HttpRequest.get(getApiUrl().toString(),
requestBuilder.getParams(), true);
+ request = httpRequestFactory.get(getApiUrl().toString(),
requestBuilder.getParams());
break;
case METHOD_POST:
- request = HttpRequest.post(getApiUrl().toString(),
requestBuilder.getParams(), true);
+ request = httpRequestFactory.post(getApiUrl().toString(),
requestBuilder.getParams());
break;
default:
throw new IllegalArgumentException("Unkown argument passed for
parameter method");
diff --git a/src/main/java/org/mediawiki/api/json/ApiHttpRequest.java
b/src/main/java/org/mediawiki/api/json/ApiHttpRequest.java
new file mode 100644
index 0000000..bb6d26a
--- /dev/null
+++ b/src/main/java/org/mediawiki/api/json/ApiHttpRequest.java
@@ -0,0 +1,24 @@
+package org.mediawiki.api.json;
+
+import java.io.IOException;
+
+/**
+ * Synchronous HTTP interface wrapper, allowing putting either a real HTTP
client
+ * or a mock object behind this interface...
+ */
+public interface ApiHttpRequest {
+ /**
+ * Read through until the remainder of the response and return the body as
a Unicode string.
+ * Will block on I/O.
+ *
+ * You can only read the body once!
+ *
+ * @return decoded body contents
+ */
+ public String body() throws ApiException;
+
+ /**
+ *
+ */
+ public void disconnect();
+}
diff --git a/src/main/java/org/mediawiki/api/json/ApiHttpRequestException.java
b/src/main/java/org/mediawiki/api/json/ApiHttpRequestException.java
new file mode 100644
index 0000000..d5bd228
--- /dev/null
+++ b/src/main/java/org/mediawiki/api/json/ApiHttpRequestException.java
@@ -0,0 +1,7 @@
+package org.mediawiki.api.json;
+
+public class ApiHttpRequestException extends Exception {
+ public ApiHttpRequestException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/src/main/java/org/mediawiki/api/json/ApiHttpRequestFactory.java
b/src/main/java/org/mediawiki/api/json/ApiHttpRequestFactory.java
new file mode 100644
index 0000000..58bae14
--- /dev/null
+++ b/src/main/java/org/mediawiki/api/json/ApiHttpRequestFactory.java
@@ -0,0 +1,9 @@
+package org.mediawiki.api.json;
+
+import java.net.URL;
+import java.util.Map;
+
+public interface ApiHttpRequestFactory {
+ public ApiHttpRequest get(CharSequence baseUrl, Map<?,?> params) throws
ApiException;
+ public ApiHttpRequest post(CharSequence baseUrl, Map<?,?> params) throws
ApiException;
+}
diff --git a/src/main/java/org/mediawiki/api/json/ApiRequest.java
b/src/main/java/org/mediawiki/api/json/ApiRequest.java
index a33f83d..a136b99 100644
--- a/src/main/java/org/mediawiki/api/json/ApiRequest.java
+++ b/src/main/java/org/mediawiki/api/json/ApiRequest.java
@@ -1,7 +1,5 @@
package org.mediawiki.api.json;
-import com.github.kevinsawicki.http.HttpRequest;
-
/**
* Encapsulates the action of performing an API call.
*
@@ -11,14 +9,14 @@
/**
* Request for which this object holds the results.
*/
- private final HttpRequest request;
+ private final ApiHttpRequest request;
/**
* Create an APIResult object corresponding to this request object.
*
- * @param request {@link HttpRequest} object which can perform the query
for which this object holds the results.
+ * @param request {@link ApiHttpRequest} object which can perform the
query for which this object holds the results.
*/
- ApiRequest(final HttpRequest request) {
+ ApiRequest(final ApiHttpRequest request) {
this.request = request;
}
@@ -36,11 +34,7 @@
* @throws ApiException Thrown in the case of a network error, or if the
response is not a JSON Array.
*/
public ApiResult send() throws ApiException {
- try {
- String body = request.body();
- return new ApiResult(body);
- } catch (HttpRequest.HttpRequestException e) {
- throw new ApiException(e.getCause());
- }
+ String body = request.body();
+ return new ApiResult(body);
}
}
diff --git a/src/main/java/org/mediawiki/api/json/RealApiHttpRequest.java
b/src/main/java/org/mediawiki/api/json/RealApiHttpRequest.java
new file mode 100644
index 0000000..7317c12
--- /dev/null
+++ b/src/main/java/org/mediawiki/api/json/RealApiHttpRequest.java
@@ -0,0 +1,26 @@
+package org.mediawiki.api.json;
+
+import com.github.kevinsawicki.http.HttpRequest;
+
+import java.io.IOException;
+
+public class RealApiHttpRequest implements ApiHttpRequest {
+ protected HttpRequest req;
+ public RealApiHttpRequest(HttpRequest req) {
+ this.req = req;
+ }
+
+ public String body() throws ApiException
+ {
+ try {
+ return req.body();
+ } catch (HttpRequest.HttpRequestException e) {
+ throw new ApiException(e.getCause());
+ }
+ }
+
+ public void disconnect()
+ {
+ req.disconnect();
+ }
+}
diff --git
a/src/main/java/org/mediawiki/api/json/RealApiHttpRequestFactory.java
b/src/main/java/org/mediawiki/api/json/RealApiHttpRequestFactory.java
new file mode 100644
index 0000000..0676298
--- /dev/null
+++ b/src/main/java/org/mediawiki/api/json/RealApiHttpRequestFactory.java
@@ -0,0 +1,15 @@
+package org.mediawiki.api.json;
+
+import com.github.kevinsawicki.http.HttpRequest;
+
+import java.util.Map;
+
+public class RealApiHttpRequestFactory implements ApiHttpRequestFactory {
+ public ApiHttpRequest get(CharSequence baseUrl, Map<?,?> params) throws
ApiException {
+ return new RealApiHttpRequest(HttpRequest.get(baseUrl, params, true));
+ }
+
+ public ApiHttpRequest post(CharSequence baseUrl, Map<?,?> params) throws
ApiException {
+ return new RealApiHttpRequest(HttpRequest.post(baseUrl, params, true));
+ }
+}
diff --git a/src/main/java/org/mediawiki/api/json/RequestBuilder.java
b/src/main/java/org/mediawiki/api/json/RequestBuilder.java
index c7e3ea3..fba7ffa 100644
--- a/src/main/java/org/mediawiki/api/json/RequestBuilder.java
+++ b/src/main/java/org/mediawiki/api/json/RequestBuilder.java
@@ -57,7 +57,7 @@
* @param method HTTP Method to use when performing the request
* @return An {@link ApiRequest} object which can be used to get the
result of this query.
*/
- private ApiRequest setupRequest(final int method) {
+ private ApiRequest setupRequest(final int method) throws ApiException {
return api.setupRequest(method, this);
}
@@ -69,7 +69,7 @@
*
* @return An {@link ApiRequest} object which can be used to get the
result of this query.
*/
- public ApiRequest get() {
+ public ApiRequest get() throws ApiException {
return setupRequest(Api.METHOD_GET);
}
@@ -81,7 +81,7 @@
*
* @return An {@link ApiRequest} object which can be used to get the
result of this query.
*/
- public ApiRequest post() {
+ public ApiRequest post() throws ApiException {
return setupRequest(Api.METHOD_POST);
}
}
diff --git a/src/test/java/org/mediawiki/api/json/ApiConstructionTest.java
b/src/test/java/org/mediawiki/api/json/ApiConstructionTest.java
index 6a6a434..71493eb 100644
--- a/src/test/java/org/mediawiki/api/json/ApiConstructionTest.java
+++ b/src/test/java/org/mediawiki/api/json/ApiConstructionTest.java
@@ -8,26 +8,27 @@
* Tests for different ways to construct the Api object.
*/
public class ApiConstructionTest {
+ public ApiHttpRequestFactory mockHttp = new
MockApiHttpRequestFactory("stub");
@Test
public void testSecure() throws Exception {
assertEquals(
- new Api("test.wikipedia.org", true).getApiUrl().toString(),
+ new Api(mockHttp, "test.wikipedia.org",
true).getApiUrl().toString(),
"https://test.wikipedia.org/w/api.php"
);
assertEquals(
- new Api("test.wikipedia.org", false).getApiUrl().toString(),
+ new Api(mockHttp, "test.wikipedia.org",
false).getApiUrl().toString(),
"http://test.wikipedia.org/w/api.php"
);
}
@Test
public void testFull() throws Exception {
assertEquals(
- new Api("test.wikipedia.org", true,
"/api.php").getApiUrl().toString(),
+ new Api(mockHttp, "test.wikipedia.org", true,
"/api.php").getApiUrl().toString(),
"https://test.wikipedia.org/api.php"
);
assertEquals(
- new Api("test.wikipedia.org", false,
"/api.php").getApiUrl().toString(),
+ new Api(mockHttp, "test.wikipedia.org", false,
"/api.php").getApiUrl().toString(),
"http://test.wikipedia.org/api.php"
);
}
@@ -35,7 +36,7 @@
@Test
public void testBasic() throws Exception {
assertEquals(
- new Api("test.wikipedia.org").getApiUrl().toString(),
+ new Api(mockHttp, "test.wikipedia.org").getApiUrl().toString(),
"https://test.wikipedia.org/w/api.php"
);
}
diff --git a/src/test/java/org/mediawiki/api/json/ApiRequestTest.java
b/src/test/java/org/mediawiki/api/json/ApiRequestTest.java
new file mode 100644
index 0000000..4194c9a
--- /dev/null
+++ b/src/test/java/org/mediawiki/api/json/ApiRequestTest.java
@@ -0,0 +1,14 @@
+package org.mediawiki.api.json;
+
+import static org.junit.Assert.*;
+import org.junit.Test;
+
+public class ApiRequestTest {
+ @Test
+ public void testEmptyMockRequest() throws ApiException {
+ ApiHttpRequest mockHttpRequest = new MockApiHttpRequest("{}");
+ ApiRequest request = new ApiRequest(mockHttpRequest);
+ ApiResult result = request.send();
+ assertNotNull(result);
+ }
+}
diff --git a/src/test/java/org/mediawiki/api/json/ApiTest.java
b/src/test/java/org/mediawiki/api/json/ApiTest.java
index af2cdb7..70ffe7c 100644
--- a/src/test/java/org/mediawiki/api/json/ApiTest.java
+++ b/src/test/java/org/mediawiki/api/json/ApiTest.java
@@ -12,10 +12,12 @@
* Tests that actually hit the API to return something.
*/
public class ApiTest {
+ // These tests hit the internet!
+ public ApiHttpRequestFactory realHttp = new RealApiHttpRequestFactory();
@Test
public void testBasicPost() throws Exception {
- Api api = new Api("test.wikipedia.org");
+ Api api = new Api(realHttp, "test.wikipedia.org");
// We're just checking if the POST goes through, so does not
// matter which username password we use
String testUsername = "doesntmatter";
@@ -33,7 +35,7 @@
@Test
public void testWrongMethod() throws Exception {
- Api api = new Api("test.wikipedia.org");
+ Api api = new Api(realHttp, "test.wikipedia.org");
JSONObject resp = api.action("login")
.get()
.send()
@@ -44,7 +46,7 @@
@Test
public void testBasicGet() throws Exception {
- Api api = new Api("test.wikipedia.org");
+ Api api = new Api(realHttp, "test.wikipedia.org");
String inputText = "Test String";
String inputTitle = "Test Title";
JSONObject resp = api.action("parse")
@@ -59,7 +61,7 @@
@Test(expected = IllegalArgumentException.class)
public void testInvalidMethod() throws Exception {
- Api api = new Api("test.wikipedia.org");
+ Api api = new Api(realHttp, "test.wikipedia.org");
api.setupRequest(404, null);
}
@@ -73,7 +75,7 @@
*/
@Test
public void testJSONException() {
- Api api = new Api("test.wikipedia.org");
+ Api api = new Api(realHttp, "test.wikipedia.org");
try {
api.action("somethingdoesnmtatter").param("format",
"xml").get().send().asObject();
} catch (ApiException e) {
@@ -92,7 +94,7 @@
*/
@Test
public void testNetworkException() {
- Api api = new Api("blashblahblahdoesnotexist");
+ Api api = new Api(realHttp, "blashblahblahdoesnotexist");
try {
api.action("somethingdoesnmtatter").param("format",
"xml").get().send().asObject();
} catch (ApiException e) {
diff --git a/src/test/java/org/mediawiki/api/json/MockApiHttpRequest.java
b/src/test/java/org/mediawiki/api/json/MockApiHttpRequest.java
new file mode 100644
index 0000000..7f98ba7
--- /dev/null
+++ b/src/test/java/org/mediawiki/api/json/MockApiHttpRequest.java
@@ -0,0 +1,28 @@
+package org.mediawiki.api.json;
+
+import com.github.kevinsawicki.http.HttpRequest;
+
+import java.io.IOException;
+
+public class MockApiHttpRequest implements ApiHttpRequest {
+ protected String responseBody;
+ protected boolean disconnected = false;
+
+ public MockApiHttpRequest(String responseBody) {
+ this.responseBody = responseBody;
+ }
+
+ public String body() throws ApiException
+ {
+ if (this.disconnected) {
+ throw new ApiException(new IOException("mock http request was
disconnected"));
+ } else {
+ return responseBody;
+ }
+ }
+
+ public void disconnect()
+ {
+ disconnected = true;
+ }
+}
diff --git
a/src/test/java/org/mediawiki/api/json/MockApiHttpRequestFactory.java
b/src/test/java/org/mediawiki/api/json/MockApiHttpRequestFactory.java
new file mode 100644
index 0000000..4cfdd03
--- /dev/null
+++ b/src/test/java/org/mediawiki/api/json/MockApiHttpRequestFactory.java
@@ -0,0 +1,20 @@
+package org.mediawiki.api.json;
+
+import java.net.URL;
+import java.util.Map;
+
+public class MockApiHttpRequestFactory implements ApiHttpRequestFactory {
+ String mockResponseText;
+
+ public MockApiHttpRequestFactory(String mockResponseText) {
+ this.mockResponseText = mockResponseText;
+ }
+
+ public ApiHttpRequest get(CharSequence baseUrl, Map<?,?> params) throws
ApiException {
+ return new MockApiHttpRequest(mockResponseText);
+ }
+
+ public ApiHttpRequest post(CharSequence baseUrl, Map<?,?> params) throws
ApiException {
+ return new MockApiHttpRequest(mockResponseText);
+ }
+}
--
To view, visit https://gerrit.wikimedia.org/r/93528
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I038f0acb7c8dc3f61980bff7999acd641b801523
Gerrit-PatchSet: 1
Gerrit-Project: apps/android/java-mwapi
Gerrit-Branch: master
Gerrit-Owner: Brion VIBBER <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits