This repository has been archived by the owner on Jul 2, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a mock library to make testing HTTP calls simpler. (#26)
* Add a mock library to make testing HTTP calls simpler. * Add a bit of documentation around the http-requests-mock library.
- Loading branch information
Showing
16 changed files
with
1,224 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,4 +17,6 @@ include::spring.adoc[] | |
|
||
include::examples.adoc[] | ||
|
||
include::testing.adoc[] | ||
|
||
include::changelog.adoc[] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
== Testing | ||
|
||
Since it is not ideal to make actual HTTP requests while performing unit tests, | ||
a mock library exists that allows tests to inject responses for expected HTTP requests | ||
made by the code being tested. Using the built-in request mocking functionality avoids | ||
the need to use dynamically generated mocking and stubbing libraries such as Mockito | ||
or Spock. | ||
|
||
=== Request Mocks | ||
|
||
A request mock contains the details about a potential HTTP request that should have a | ||
mocked response. The `RequestMock` object contains most of the same properties that | ||
a typical `HttpRequest` does, such as request URI, headers, and query parameters, | ||
as well as some additional properties, such as the HTTP method that should match. | ||
|
||
It also includes properties about the response, such as response status code, headers, | ||
and the response entity. | ||
|
||
The minimal requirement for a mock to match a request is that the URI must match the | ||
request. If a mock has other details set, such as request headers, HTTP method, or | ||
query parameters, those also must match. If they are not specified in the mock, they | ||
are not considered as part of the match criteria. This allows tests to be as specific | ||
or not as they need to be. | ||
|
||
Mocks that match requests will have their counters incremented each time a match occurs. | ||
The `RequestMock` object exposes these details via the `called` and `getCalledCount` | ||
methods. | ||
|
||
NOTE: If a no matching mock is found for an HTTP request, and `UnmatchedRequestMockException` | ||
will be thrown. | ||
|
||
=== MockHttpClientFactory | ||
|
||
The `MockHttpClientFactory` class may be used in tests and injected into those objects | ||
being tested. This factory implementation exposes the same interface as the other provider | ||
libraries, and adds some additional methods specific to testing. Request mocks are | ||
created via the `createMock` method, which returns and tracks an empty mock which may | ||
be configured. | ||
|
||
The factory exposes a method `allMocksCalled` that will return whether all created mocks | ||
have been called at least once. | ||
|
||
=== Example | ||
|
||
Below is a simple Java object that makes an API request and returns the payload, | ||
which is expected to be a simple `String`. | ||
|
||
[source,java] | ||
---- | ||
/** | ||
* A test object that makes use of the HTTP requests library to fetch | ||
* a string result from the API located at http://localhost/api. | ||
*/ | ||
class TestObject { | ||
private final HttpClientFactory httpClientFactory; | ||
public TestObject(HttpClientFactory httpClientFactory) { | ||
this.httpClientFactory = httpClientFactory; | ||
} | ||
public String makeApiCall() { | ||
return httpClientFactory.createHttpClient.get("http://localhost/api").getEntity(String.class); | ||
} | ||
} | ||
---- | ||
|
||
This class may be tested using a framework, such as Junit or Spock. A Spock | ||
example follows: | ||
|
||
[source,groovy] | ||
---- | ||
class FunctionalSpec extends Specification { | ||
/** | ||
* Mock HTTP client factory. | ||
*/ | ||
MockHttpClientFactory httpClientFactory | ||
/** | ||
* Entity converter manager. | ||
*/ | ||
EntityConverterManager entityConverterManager | ||
/** | ||
* Configure the environment before each test. | ||
*/ | ||
def setup() { | ||
// Create an entity converter manager with the Groovy JSON converters | ||
entityConverterManager = new EntityConverterManager([ | ||
new JsonEntityWriter(), | ||
new JsonEntityReader() | ||
]) | ||
// Create the mock HTTP client factory with the entity converter manager above | ||
httpClientFactory = new MockHttpClientFactory(entityConverterManager) | ||
} | ||
def 'A simple mocked GET request returns the expected response entity'() { | ||
setup: | ||
// Create a mock for a specific URL with a response payload | ||
RequestMock mock = httpClientFactory | ||
.createMock() | ||
.setRequestUri('http://localhost/foo/bar') | ||
.setResponseEntity([ | ||
foo: 'bar', | ||
baz: 'boz' | ||
]) | ||
TestObject object = new TestObject(httpClientFactory) | ||
when: | ||
// Make the API call through the test object | ||
def response = object.makeApiCall() | ||
then: | ||
// The mock should have been called at least once | ||
mock.called() | ||
// Since the above mock is the only one that was created, all mocks | ||
// should have been called from the client factory. | ||
httpClientFactory.allMocksCalled() | ||
// The response should have been returned and converted properly | ||
response == [ | ||
foo: 'bar', | ||
baz: 'boz' | ||
] | ||
} | ||
} | ||
---- | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
/* | ||
* Copyright 2016-2020 the original author or authors. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
apply from: "${rootDir}/gradle/library-publish.gradle" | ||
|
||
apply plugin: "groovy" | ||
|
||
dependencies { | ||
api project(':http-requests-core') | ||
|
||
testImplementation "org.codehaus.groovy:groovy:${groovyVersion}" | ||
testImplementation "org.spockframework:spock-core:${spockVersion}" | ||
testImplementation project(':http-requests-groovy') | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
# | ||
# Copyright 2016-2018 the original author or authors. | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
# | ||
projectDescription=Mock requests with HTTP Requests. |
61 changes: 61 additions & 0 deletions
61
http-requests-mock/src/main/java/com/budjb/httprequests/test/MockHttpClient.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
/* | ||
* Copyright 2016-2018 the original author or authors. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package com.budjb.httprequests.test; | ||
|
||
import com.budjb.httprequests.*; | ||
import com.budjb.httprequests.converter.EntityConverterManager; | ||
import com.budjb.httprequests.filter.HttpClientFilterProcessor; | ||
|
||
import java.io.IOException; | ||
|
||
/** | ||
* An implementation of {@link HttpClient} that uses the Jersey Client 1.x library. | ||
*/ | ||
public class MockHttpClient extends AbstractHttpClient { | ||
/** | ||
* Mock HTTP client factory. | ||
*/ | ||
private final MockHttpClientFactory httpClientFactory; | ||
|
||
/** | ||
* Constructor. | ||
* | ||
* @param converterManager Entity converter manager. | ||
*/ | ||
MockHttpClient(MockHttpClientFactory httpClientFactory, EntityConverterManager converterManager) { | ||
super(converterManager); | ||
this.httpClientFactory = httpClientFactory; | ||
} | ||
|
||
/** | ||
* {@inheritDoc} | ||
*/ | ||
@Override | ||
protected HttpResponse execute(HttpContext context, HttpEntity entity, HttpClientFilterProcessor filterProcessor) throws IOException { | ||
HttpRequest request = context.getRequest(); | ||
HttpMethod method = context.getMethod(); | ||
|
||
RequestMock mock = httpClientFactory.findMatchingMock(context.getRequest(), context.getMethod()); | ||
|
||
if (mock == null) { | ||
throw new UnmatchedRequestMockException(request, method); | ||
} | ||
|
||
mock.incrementCalled(); | ||
|
||
return new MockHttpResponse(request, getConverterManager(), mock); | ||
} | ||
} |
Oops, something went wrong.