├── .github └── workflows │ └── maven.yml ├── .gitignore ├── .travis.yml ├── HttpClientMock.iml ├── LICENSE ├── README.md ├── pom.xml └── src ├── main └── java │ └── com │ └── github │ └── paweladamski │ └── httpclientmock │ ├── Debugger.java │ ├── HttpClientMock.java │ ├── HttpClientMockBuilder.java │ ├── HttpClientResponseBuilder.java │ ├── HttpClientVerify.java │ ├── HttpClientVerifyBuilder.java │ ├── HttpResponseProxy.java │ ├── Request.java │ ├── Rule.java │ ├── RuleBuilder.java │ ├── UrlConditions.java │ ├── UrlParamsParser.java │ ├── UrlParser.java │ ├── action │ ├── Action.java │ ├── CookieAction.java │ ├── ExceptionAction.java │ ├── HeaderAction.java │ ├── StatusWithEmptyEntityResponse.java │ ├── StatusWithNullEntityResponse.java │ ├── StringResponse.java │ └── UrlEncodedFormEntityResponse.java │ ├── condition │ ├── BodyMatcher.java │ ├── Condition.java │ ├── HeaderCondition.java │ ├── HttpMethodCondition.java │ ├── UrlEncodedFormCondition.java │ └── UrlEncodedFormParser.java │ └── matchers │ ├── HttpResponseMatchers.java │ ├── MatchersList.java │ ├── ParametersMatcher.java │ └── UrlQueryMatcher.java └── test └── java └── com └── github └── paweladamski └── httpclientmock ├── Asserts.java ├── DebuggingTest.java ├── HttpClientMockBuilderTest.java ├── HttpClientMockBuilder_doReturnStatusTest.java ├── HttpClientMockBuilder_doReturnWithStatusTest.java ├── HttpClientMockTest.java ├── HttpClientResponseBuilderTest.java ├── HttpClientVerifyTest.java ├── Requests.java ├── ThrowingRunnable.java ├── UrlParamsParserTest.java ├── UrlParserTest.java ├── action └── UrlEncodedFormEntityResponseTest.java ├── condition └── UrlEncodedFormConditionTest.java └── matchers └── ParametersMatcherTest.java /.github/workflows/maven.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v1 12 | - name: Set up JDK 1.8 13 | uses: actions/setup-java@v1 14 | with: 15 | java-version: 1.8 16 | - name: Build with Maven 17 | run: mvn package --file pom.xml 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | HttpClientMock.iml 3 | .idea/ 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | -------------------------------------------------------------------------------- /HttpClientMock.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Paweł Adamski 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Maven Central](https://github.com/PawelAdamski/HttpClientMock/workflows/Build/badge.svg) ![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.github.paweladamski/HttpClientMock/badge.svg) 2 | 3 | # HttpClientMock 4 | 5 | HttpClientMock is a library for mocking [Apache HttpClient](https://hc.apache.org/httpcomponents-client-4.5.x/httpclient/apidocs/index.html). It has an intuitive API for defining client behaviour and verifying number of made requests. It works with HttpClient 4.X and 5.X. 6 | 7 | * [Installation](#installation) 8 | * [Usage](#usage) 9 | * [Request matching](#request-matching) 10 | * [Define response](#define-response) 11 | * [Verification](#verification) 12 | * [Matching query and form parameters](#matching-query-and-form-parameters) 13 | * [Debugging](#debugging) 14 | * [Example 1](#example-1) 15 | * [Example 2](#example-2) 16 | * [Release notes](#release-notes) 17 | 18 | 19 | ## Installation 20 | HttpClientMock is available in Maven Central Repository. [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.github.paweladamski/HttpClientMock/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.github.paweladamski/HttpClientMock) 21 | * For mocking HttpClient 4.x use HttpClientMock 1.X. 22 | * For mocking HttpClient 5.x use HttpClientMock 2.X. 23 | ## Usage 24 | 25 | #### Record 26 | Working with HttpClientMock starts with defining client behaviour. Before code under tests starts HttpClientMock must know how to respond to every request. 27 | ``` 28 | HttpClientMock httpClientMock = new HttpClientMock(); 29 | httpClientMock.onGet("http://localhost/login") 30 | .withParameter("user","john") 31 | .doReturn("Ok"); 32 | httpClientMock.onPost("http://localhost/login").doReturnStatus(501); 33 | ``` 34 | 35 | #### Replay 36 | Code under test starts and uses HttpClientMock with defined behaviour. 37 | ``` 38 | httpClient.execute(new HttpGet("http://localhost/login?user:john")); // returns response with body "Ok" 39 | httpClient.execute(new HttpPost("http://localhost/login")); // returns response with status 501 40 | ``` 41 | 42 | #### Verify 43 | When code under test finishes, HttpClientMock allows to check number of made request. It is possible to use the same set of conditions as for defining mock behaviour. 44 | ``` 45 | httpClientMock.verify().get("http://localhost/login").withParameter("user","john").called(); 46 | httpClientMock.verify().post("http://localhost/login").notCalled(); 47 | ``` 48 | 49 | 50 | ## Request matching 51 | 52 | ### HTTP method 53 | HttpClientMock supports all Http methods. 54 | ``` 55 | httpClientMock.onGet().doReturn("get"); 56 | httpClientMock.onPost().doReturn("post"); 57 | httpClientMock.onPut().doReturn("put"); 58 | httpClientMock.onDelete().doReturn("delete"); 59 | httpClientMock.onOptions().doReturn("options"); 60 | httpClientMock.onHead().doReturn("head"); 61 | ``` 62 | ### URL 63 | Every `onGet()`, `onPost()`, .... method accept URL. It is possible to write: 64 | ``` 65 | httpClientMock.onGet("http://localhost/login?user=john").doReturnStatus(200); 66 | ``` 67 | which is equal to 68 | ``` 69 | httpClientMock.onGet() 70 | .withHost("http://localhost") 71 | .withPath("/login") 72 | .withParameter("user","john") 73 | .doReturnStatus(200); 74 | ``` 75 | 76 | It is possible to define default host using HttpClientMock constructor, so later methods can accept relative URL-s. 77 | ``` 78 | HttpClientMock httpClientMock = new HttpClientMock("http://localhost"); 79 | httpClientMock.onGet("/login").doReturn("ok"); 80 | httpClientMock.onPost("/edit?user=john").doReturnStatus(200); 81 | 82 | httpClientMock.onGet("http://www.google.com").doReturn("Google") // Absolute paths still work. 83 | ``` 84 | 85 | ### Host, path, parameters conditions 86 | It is possible to define each part of url separately. 87 | ``` 88 | httpClientMock.onGet() 89 | .withHost("http://localhost") 90 | .withPath("/login") 91 | .withParameter("user","john") 92 | .doReturnStatus(200); 93 | ``` 94 | 95 | ### Header condition 96 | ``` 97 | httpClientMock.onGet("http://localhost/login") 98 | .withHeader("tracking","123") 99 | .doReturn("ok"); 100 | ``` 101 | 102 | ### Form parameters 103 | ``` 104 | httpClientMock.onPost("/login") 105 | .withFormParameter("username", "John") 106 | .withFormParameter("password", Matchers.containsString("secret")) 107 | .doReturnStatus(200); 108 | ``` 109 | 110 | ### Body condition 111 | ``` 112 | httpClientMock.onGet("http://localhost/login") 113 | .withBody("tracking",containsString("123")) 114 | .doReturn("ok"); 115 | ``` 116 | 117 | ### Custom condition 118 | ``` 119 | Condition fooCondition = request -> request.getUri().contains("foo"); 120 | httpClientMock.onGet("http://localhost/foo/bar") 121 | .with(fooCondition) 122 | .doReturn("yes"); 123 | ``` 124 | 125 | ### Matchers 126 | Every condition method accepts [Hamcrest Matcher](https://github.com/hamcrest/JavaHamcrest) which allows to define custom conditions on requests. 127 | ``` 128 | httpClientMock.onGet("http://localhost") 129 | .withPath(containsString("login")) 130 | .withParameter("user",equalToIgnoringCase("John")); 131 | ``` 132 | 133 | ### Multiple matching rules 134 | If request matches more then one rule, then last defined one is used. 135 | 136 | ### None rule matche 137 | If request doesn't matche any rule, HttpClientMock return response with status 404. 138 | 139 | ## Define response 140 | 141 | ### Response 142 | Response with provided body and status 200. 143 | ``` 144 | httpClientMock.onGet("http://localhost").doReturn("my response"); 145 | ``` 146 | ### Status 147 | Response with empty body and provided status 148 | ``` 149 | httpClientMock.onGet("http://localhost").doReturnStatus(300); 150 | httpClientMock.onGet("http://localhost").doReturn("Overloaded").withStatus(500); 151 | ``` 152 | ### Exception 153 | Instead of returning response it throws defined exception. 154 | ``` 155 | httpClientMock.onGet("http://localhost").doThrowException(new IOException()); 156 | ``` 157 | ### Custom action 158 | ``` 159 | Action echo = r -> { 160 | HttpEntity entity = ((HttpEntityEnclosingRequestBase) r.getHttpRequest()).getEntity(); 161 | BasicHttpResponse response = new BasicHttpResponse(new ProtocolVersion("http", 1, 1), 200, "ok"); 162 | response.setEntity(entity); 163 | return response; 164 | }; 165 | httpClientMock.onGet("http://localhost").doAction(echo); 166 | ``` 167 | ### Response header 168 | ``` 169 | httpClientMock.onPost("/login").doReturn("foo").withHeader("tracking", "123"); 170 | ``` 171 | ### Response status 172 | ``` 173 | httpClientMock.onPost("/login?user=bar").doReturn("Wrong user").withStatus(403); 174 | ``` 175 | 176 | ### JSON 177 | Response with provided body, status 200 and content type "application/json" 178 | ``` 179 | httpClientMock.onPost("/login").doReturnJSON("{foo:1}"); 180 | ``` 181 | 182 | ### XML 183 | Response with provided body, status 200 and content type "application/xml" 184 | ``` 185 | httpClientMock.onPost("/login").doReturnXML("bar"); 186 | ``` 187 | 188 | ### Multiple actions 189 | It is possible to add multiple actions to one rule. Every call will use next action until last is reached. 190 | ``` 191 | httpClientMock.onPut("/addUser") 192 | .doReturn("ok") 193 | .doReturnStatus(500); 194 | 195 | httpClientMock.execute(new HttpPut("http://localhost/addUser")); //returns "ok" 196 | httpClientMock.execute(new HttpPut("http://localhost/addUser")); //returns status 500 197 | httpClientMock.execute(new HttpPut("http://localhost/addUser")); //returns status 500 198 | ``` 199 | 200 | 201 | ## Verification 202 | HttpClientMock allows to check how many calls were made. Verification supports the same set of conditions us rule defining. 203 | ``` 204 | httpClientMock.verify().get("http://localhost").called(); 205 | 206 | httpClientMock.verify().get("http://localhost/login") 207 | .withParameter("user","john") 208 | .called(); 209 | 210 | httpClientMock.verify().get("http://localhost/login") 211 | .withParameter("user","Ben") 212 | .notCalled(); 213 | 214 | httpClientMock.verify().delete().notCalled(); 215 | 216 | httpClientMock.verify().get().called(greaterThanOrEqualTo(1)); 217 | 218 | ``` 219 | 220 | ## Matching query and form parameters 221 | There are two methods that control HttpClientMock behaviour when request contains extra form 222 | or query parameters: 223 | - `withExtraParameters`: allows request to contain extra query parameters 224 | - `withoutExtraParameters`: disallows request to contain extra query parameters 225 | - `withExtraFormParameters`: allows request to contain extra form parameters 226 | - `withoutExtraFormParameters`: disallows request to contain extra form parameters 227 | 228 | Examples: 229 | ``` 230 | httpClientMock.onPost("/login") 231 | .withParameter("user","John") 232 | .withoutExtraParameters() 233 | .doReturn("ok"); 234 | ``` 235 | Above condition will not match request `http://www.example.com/login?user=John&password=secret` because 236 | it contains extra parameter `password`. 237 | 238 | ``` 239 | httpClientMock.onPost("/login") 240 | .withParameter("user","John") 241 | .withExtraParameters() 242 | .doReturn("ok"); 243 | ``` 244 | Above condition will match request `http://www.example.com/login?user=John&password=secret` although 245 | it contains extra parameter `password`. 246 | 247 | By default HttpClientMock matches requests with extra form and query parameters. 248 | 249 | ## Debugging 250 | HttpClientMock can help you to debug your code by displaying information which matchers matched your request. 251 | You can use `HttpClientMock#debugOn` to turn it on and `HttpClientMock#debugOff` to turn it off. 252 | Example message: 253 | ``` 254 | Rule 1: 255 | MATCHES EXPECTED 256 | true HTTP method is GET 257 | true schema is "http" 258 | true host is "localhost" 259 | false path is "/login" 260 | true port is empty 261 | ``` 262 | 263 | 264 | 265 | ## Example 1 266 | ``` 267 | // DEFINE BEHAVIOUR 268 | HttpClientMock httpClientMock = new HttpClientMock("http://localhost:8080"); 269 | httpClientMock.onGet("/login?user=john").doReturnJSON("{permission:1}"); 270 | httpClientMock.onPost("/edit") 271 | .withParameter("user","John") 272 | .doReturn("ok") 273 | .doReturnStatus(503); 274 | 275 | // EXECUTION 276 | // request to http://localhost:8080/login?user=john returns JSON {permission:1} 277 | // first request to http://localhost:8080/edit?user=john returns message "ok" 278 | // second request to http://localhost:8080/edit?user=john returns request with status 503 279 | 280 | // VERIFICATION 281 | httpClientMock.verify().get("/login?user=john").called(); 282 | httpClientMock.verify().post("/edit?user=john").called(2); 283 | httpClientMock.verify().delete().notCalled(); 284 | ``` 285 | 286 | 287 | ## Example 2 288 | ``` 289 | // DEFINE BEHAVIOUR 290 | HttpClientMock httpClientMock = new HttpClientMock(); 291 | httpClientMock.onGet("http://localhost:8080/login").doReturn("Missing parameter user").withStatus(400); 292 | httpClientMock.onGet("http://localhost:8080/login") 293 | .withParameter("user","JJohn") 294 | .doReturn("Wrong user name").withStatus(403); 295 | httpClientMock.onGet("http://localhost:8080/login") 296 | .withParameter("user","John") 297 | .doReturn("ok"); 298 | 299 | // EXECUTION 300 | // request to http://localhost:8080/login?user=john returns message "ok" 301 | 302 | // VERIFICATION 303 | httpClientMock.verify().get("/login?user=john").called(); 304 | ``` 305 | 306 | ## Release notes 307 | 308 | 2.1.1 309 | - Fixed MalformedURLException when using not absolute paths 310 | 311 | 2.1.0 312 | - Added method `withStatus` accepting status code and text 313 | 314 | 2.0.0 315 | - Added support for HttpClient 5. 316 | - Removed methods for mocking URL reference 317 | 318 | 1.9.1 319 | - Fixed MalformedURLException when using not absolute paths 320 | 321 | 1.9.0 322 | - Added method `withStatus` accepting status code and text 323 | 324 | 1.8.0 325 | - Added methods {`withExtraParameters`, `withoutExtraParameters`, `withExtraFormParameters`, `withoutExtraFormParameters`} to better control form and query parameters matching. 326 | *WARNING* Breaking changes: Since this version by default HttpClientMock matches requests with extra form and query parameters. 327 | 328 | 1.7.0 329 | - Added methods (`withFormParameter`, `withFormParameters`) for matching form parameters (URL encode parameters). 330 | - Added action (`doReturnFormParams`) which return response with body containing provided form parameters. 331 | 332 | 1.6.0 333 | * Added possibility to set response `Content-Type`. 334 | * Fixed wrong `Contet-Type` in methods 'doReturnXML', `doReturnJSON` 335 | 336 | 1.5.0 337 | * Added possibility to add cookies to the response. 338 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | com.github.paweladamski 6 | HttpClientMock 7 | 1.10.0 8 | jar 9 | 10 | ${project.groupId}:${project.artifactId} 11 | Library for mocking Apache HttpClient. 12 | https://github.com/PawelAdamski/HttpClientMock 13 | 14 | 15 | MIT License 16 | http://www.opensource.org/licenses/mit-license.php 17 | 18 | 19 | 20 | 21 | Paweł Adamski 22 | pawel.poczta@gmail.com 23 | 24 | 25 | 26 | scm:git:git://github.com/paweladamski/HttpClientMock.git 27 | scm:git:ssh://github.com:PawelAdamski/HttpClientMock.git 28 | http://github.com/PawelAdamski/HttpClientMock/tree/master 29 | 30 | 31 | 32 | UTF-8 33 | 1.8 34 | 1.8 35 | 36 | 37 | 38 | 39 | 40 | org.apache.httpcomponents 41 | httpclient 42 | 4.5.13 43 | 44 | 45 | org.junit.jupiter 46 | junit-jupiter-params 47 | 5.7.0 48 | test 49 | 50 | 51 | org.hamcrest 52 | hamcrest-all 53 | 1.3 54 | 55 | 56 | org.hamcrest 57 | hamcrest-core 58 | 1.3 59 | 60 | 61 | 62 | 63 | 64 | ossrh 65 | https://oss.sonatype.org/content/repositories/snapshots 66 | 67 | 68 | ossrh 69 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 70 | 71 | 72 | 73 | 74 | 75 | release 76 | 77 | 78 | 79 | org.sonatype.plugins 80 | nexus-staging-maven-plugin 81 | 1.6.7 82 | true 83 | 84 | ossrh 85 | https://oss.sonatype.org/ 86 | true 87 | 88 | 89 | 90 | org.apache.maven.plugins 91 | maven-source-plugin 92 | 2.2.1 93 | 94 | 95 | attach-sources 96 | 97 | jar-no-fork 98 | 99 | 100 | 101 | 102 | 103 | org.apache.maven.plugins 104 | maven-javadoc-plugin 105 | 2.9.1 106 | 107 | 108 | attach-javadocs 109 | 110 | jar 111 | 112 | 113 | 114 | 115 | 116 | org.apache.maven.plugins 117 | maven-gpg-plugin 118 | 1.5 119 | 120 | 121 | sign-artifacts 122 | verify 123 | 124 | sign 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | -------------------------------------------------------------------------------- /src/main/java/com/github/paweladamski/httpclientmock/Debugger.java: -------------------------------------------------------------------------------- 1 | package com.github.paweladamski.httpclientmock; 2 | 3 | import java.util.List; 4 | import org.apache.http.HttpRequest; 5 | 6 | public class Debugger { 7 | 8 | public Rule debug(List rules, Request request) { 9 | logRequest(request); 10 | logRules(rules, request); 11 | return Rule.NOT_FOUND; 12 | } 13 | 14 | private void logRules(List rules, Request request) { 15 | if (rules.size() == 0) { 16 | System.out.println("No rules were defined."); 17 | } 18 | for (int i = 0; i < rules.size(); i++) { 19 | System.out.println("Rule " + (i + 1) + ":"); 20 | System.out.println("\tMATCHES\t\tEXPECTED"); 21 | rules.get(i).debug(request, this); 22 | } 23 | System.out.println(); 24 | System.out.println("----------------"); 25 | 26 | } 27 | 28 | private void logRequest(Request request) { 29 | HttpRequest httpRequest = request.getHttpRequest(); 30 | System.out.println("Request: " + httpRequest.getRequestLine().getMethod() + " " + httpRequest.getRequestLine().getUri()); 31 | } 32 | 33 | public void message(boolean matches, String expected) { 34 | String debugMessage = String.format("\t%s\t\t%s", matches, expected); 35 | System.out.println(debugMessage); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/github/paweladamski/httpclientmock/HttpClientMock.java: -------------------------------------------------------------------------------- 1 | package com.github.paweladamski.httpclientmock; 2 | 3 | import java.io.IOException; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | import org.apache.http.HttpException; 7 | import org.apache.http.HttpHost; 8 | import org.apache.http.HttpRequest; 9 | import org.apache.http.HttpRequestInterceptor; 10 | import org.apache.http.HttpResponse; 11 | import org.apache.http.HttpResponseInterceptor; 12 | import org.apache.http.client.methods.CloseableHttpResponse; 13 | import org.apache.http.client.methods.HttpDelete; 14 | import org.apache.http.client.methods.HttpGet; 15 | import org.apache.http.client.methods.HttpHead; 16 | import org.apache.http.client.methods.HttpOptions; 17 | import org.apache.http.client.methods.HttpPatch; 18 | import org.apache.http.client.methods.HttpPost; 19 | import org.apache.http.client.methods.HttpPut; 20 | import org.apache.http.conn.ClientConnectionManager; 21 | import org.apache.http.impl.client.CloseableHttpClient; 22 | import org.apache.http.params.BasicHttpParams; 23 | import org.apache.http.params.HttpParams; 24 | import org.apache.http.protocol.HttpContext; 25 | 26 | public class HttpClientMock extends CloseableHttpClient { 27 | 28 | private final HttpParams params = new BasicHttpParams(); 29 | private final Debugger debugger; 30 | 31 | private final List rulesUnderConstruction = new ArrayList<>(); 32 | private final List rules = new ArrayList<>(); 33 | private final String defaultHost; 34 | private final List requests = new ArrayList<>(); 35 | private boolean isDebuggingTurnOn = false; 36 | 37 | private final List requestInterceptors = new ArrayList<>(); 38 | private final List responseInterceptors = new ArrayList<>(); 39 | 40 | /** 41 | * Creates mock of Apache HttpClient 42 | */ 43 | public HttpClientMock() { 44 | this(""); 45 | } 46 | 47 | /** 48 | * Creates mock of Apache HttpClient with default host. All defined conditions without host will use default host 49 | * 50 | * @param defaultHost default host for later conditions 51 | */ 52 | public HttpClientMock(String defaultHost) { 53 | this(defaultHost, new Debugger()); 54 | } 55 | 56 | /** 57 | * Creates mock of Apache HttpClient with default host. All defined conditions without host will use default host 58 | * 59 | * @param defaultHost default host for later conditions 60 | * @param debugger debugger used for testing 61 | */ 62 | HttpClientMock(String defaultHost, Debugger debugger) { 63 | this.defaultHost = defaultHost; 64 | this.debugger = debugger; 65 | } 66 | 67 | /** 68 | * Resets mock to initial state where there are no rules and no previous requests. 69 | */ 70 | public void reset() { 71 | this.rulesUnderConstruction.clear(); 72 | this.rules.clear(); 73 | this.requests.clear(); 74 | } 75 | 76 | /** 77 | * Creates verification builder. 78 | * 79 | * @return request number verification builder 80 | */ 81 | public HttpClientVerify verify() { 82 | return new HttpClientVerify(defaultHost, requests); 83 | } 84 | 85 | /** 86 | * Starts defining new rule which requires HTTP POST method. 87 | * 88 | * @return HttpClientMockBuilder which allows to define new rule 89 | */ 90 | public HttpClientMockBuilder onPost() { 91 | return newRule(HttpPost.METHOD_NAME); 92 | } 93 | 94 | /** 95 | * Starts defining new rule which requires HTTP GET method. 96 | * 97 | * @return HttpClientMockBuilder which allows to define new rule 98 | */ 99 | public HttpClientMockBuilder onGet() { 100 | return newRule(HttpGet.METHOD_NAME); 101 | } 102 | 103 | /** 104 | * Starts defining new rule which requires HTTP DELETE method. 105 | * 106 | * @return HttpClientMockBuilder which allows to define new rule 107 | */ 108 | public HttpClientMockBuilder onDelete() { 109 | return newRule(HttpDelete.METHOD_NAME); 110 | } 111 | 112 | /** 113 | * Starts defining new rule which requires HTTP HEAD method. 114 | * 115 | * @return HttpClientMockBuilder which allows to define new rule 116 | */ 117 | public HttpClientMockBuilder onHead() { 118 | return newRule(HttpHead.METHOD_NAME); 119 | } 120 | 121 | /** 122 | * Starts defining new rule which requires HTTP OPTIONS method. 123 | * 124 | * @return HttpClientMockBuilder which allows to define new rule 125 | * @deprecated Method name contains misspelling, use {@link #onOptions} 126 | */ 127 | @Deprecated 128 | public HttpClientMockBuilder onOption() { 129 | return onOptions(); 130 | } 131 | 132 | /** 133 | * Starts defining new rule which requires HTTP OPTIONS method. 134 | * 135 | * @return HttpClientMockBuilder which allows to define new rule 136 | */ 137 | public HttpClientMockBuilder onOptions() { 138 | return newRule(HttpOptions.METHOD_NAME); 139 | } 140 | 141 | /** 142 | * Starts defining new rule which requires HTTP PUT method. 143 | * 144 | * @return HttpClientMockBuilder which allows to define new rule 145 | */ 146 | public HttpClientMockBuilder onPut() { 147 | return newRule(HttpPut.METHOD_NAME); 148 | } 149 | 150 | /** 151 | * Starts defining new rule which requires HTTP PATCH method. 152 | * 153 | * @return HttpClientMockBuilder which allows to define new rule 154 | */ 155 | public HttpClientMockBuilder onPatch() { 156 | return newRule(HttpPatch.METHOD_NAME); 157 | } 158 | 159 | /** 160 | * Starts defining new rule which requires HTTP GET method and url. If provided url starts with "/" request url must be equal to concatenation of default host 161 | * and url. Otherwise request url must equal to provided url. If provided url contains query parameters and/or reference they are parsed and added as a 162 | * separate conditions.

For example:
httpClientMock.onGet("http://localhost/login?user=Ben#edit");
is equal to
163 | * httpClientMock.onGet("http://localhost/login").withParameter("user","Ben").withReference("edit); 164 | * 165 | * @param url required url 166 | * @return HttpClientMockBuilder which allows to define new rule 167 | */ 168 | public HttpClientMockBuilder onGet(String url) { 169 | return newRule(HttpGet.METHOD_NAME, url); 170 | } 171 | 172 | /** 173 | * Starts defining new rule which requires HTTP POST method and url. URL works the same way as in {@link #onGet(String) onGet} 174 | * 175 | * @param url required url 176 | * @return HttpClientMockBuilder which allows to define new rule 177 | */ 178 | public HttpClientMockBuilder onPost(String url) { 179 | return newRule(HttpPost.METHOD_NAME, url); 180 | } 181 | 182 | /** 183 | * Starts defining new rule which requires HTTP PUT method and url. URL works the same way as in {@link #onGet(String) onGet} 184 | * 185 | * @param url required url 186 | * @return HttpClientMockBuilder which allows to define new rule 187 | */ 188 | public HttpClientMockBuilder onPut(String url) { 189 | return newRule(HttpPut.METHOD_NAME, url); 190 | } 191 | 192 | /** 193 | * Starts defining new rule which requires HTTP DELETE method and url. URL works the same way as in {@link #onGet(String) onGet} 194 | * 195 | * @param url required url 196 | * @return HttpClientMockBuilder which allows to define new rule 197 | */ 198 | public HttpClientMockBuilder onDelete(String url) { 199 | return newRule(HttpDelete.METHOD_NAME, url); 200 | } 201 | 202 | /** 203 | * Starts defining new rule which requires HTTP HEAD method and url. URL works the same way as in {@link #onGet(String) onGet} 204 | * 205 | * @param url required url 206 | * @return HttpClientMockBuilder which allows to define new rule 207 | */ 208 | public HttpClientMockBuilder onHead(String url) { 209 | return newRule(HttpHead.METHOD_NAME, url); 210 | } 211 | 212 | /** 213 | * Starts defining new rule which requires HTTP OPTIONS method and url. URL works the same way as in {@link #onGet(String) onGet} 214 | * 215 | * @param url required url 216 | * @return HttpClientMockBuilder which allows to define new rule 217 | */ 218 | public HttpClientMockBuilder onOptions(String url) { 219 | return newRule(HttpOptions.METHOD_NAME, url); 220 | } 221 | 222 | /** 223 | * Starts defining new rule which requires HTTP PATCH method and url. URL works the same way as in {@link #onGet(String) onGet} 224 | * 225 | * @param url required url 226 | * @return HttpClientMockBuilder which allows to define new rule 227 | */ 228 | public HttpClientMockBuilder onPatch(String url) { 229 | return newRule(HttpPatch.METHOD_NAME, url); 230 | } 231 | 232 | private HttpClientMockBuilder newRule(String method) { 233 | RuleBuilder r = new RuleBuilder(method); 234 | rulesUnderConstruction.add(r); 235 | return new HttpClientMockBuilder(r); 236 | } 237 | 238 | private HttpClientMockBuilder newRule(String method, String url) { 239 | RuleBuilder r = new RuleBuilder(method, defaultHost, url); 240 | rulesUnderConstruction.add(r); 241 | return new HttpClientMockBuilder(r); 242 | } 243 | 244 | @Override 245 | protected CloseableHttpResponse doExecute(HttpHost httpHost, HttpRequest httpRequest, HttpContext httpContext) throws IOException { 246 | finishBuildingRules(); 247 | executeRequestInterceptors(httpRequest, httpContext); 248 | HttpResponse response = getHttpResponse(httpHost, httpRequest, httpContext); 249 | executeResponseInterceptors(httpContext, response); 250 | return new HttpResponseProxy(response); 251 | } 252 | 253 | private void executeResponseInterceptors(HttpContext httpContext, HttpResponse response) throws IOException { 254 | try { 255 | for (HttpResponseInterceptor responseInterceptor : responseInterceptors) { 256 | responseInterceptor.process(response, httpContext); 257 | } 258 | } catch (HttpException e) { 259 | throw new IOException(e); 260 | } 261 | } 262 | 263 | private HttpResponse getHttpResponse(HttpHost httpHost, HttpRequest httpRequest, HttpContext httpContext) throws IOException { 264 | Request request = new Request(httpHost, httpRequest, httpContext); 265 | requests.add(request); 266 | Rule rule = rules.stream() 267 | .filter(r -> r.matches(httpHost, httpRequest, httpContext)) 268 | .reduce((a, b) -> b) 269 | .orElse(Rule.NOT_FOUND); 270 | if (isDebuggingTurnOn || rule == Rule.NOT_FOUND) { 271 | debugger.debug(rules, request); 272 | } 273 | return rule.nextResponse(request); 274 | } 275 | 276 | private void executeRequestInterceptors(HttpRequest httpRequest, HttpContext httpContext) throws IOException { 277 | try { 278 | for (HttpRequestInterceptor requestInterceptor : requestInterceptors) { 279 | requestInterceptor.process(httpRequest, httpContext); 280 | } 281 | } catch (HttpException e) { 282 | throw new IOException(e); 283 | } 284 | } 285 | 286 | private void finishBuildingRules() { 287 | synchronized (rulesUnderConstruction) { 288 | for (RuleBuilder ruleBuilder : rulesUnderConstruction) { 289 | rules.add(ruleBuilder.toRule()); 290 | } 291 | rulesUnderConstruction.clear(); 292 | } 293 | } 294 | 295 | @Override 296 | public void close() throws IOException { 297 | } 298 | 299 | @Override 300 | public HttpParams getParams() { 301 | return params; 302 | } 303 | 304 | @Override 305 | public ClientConnectionManager getConnectionManager() { 306 | return null; 307 | } 308 | 309 | public void debugOn() { 310 | isDebuggingTurnOn = true; 311 | } 312 | 313 | public void debugOff() { 314 | isDebuggingTurnOn = false; 315 | } 316 | 317 | public void addRequestInterceptor(HttpRequestInterceptor requestInterceptor) { 318 | this.requestInterceptors.add(requestInterceptor); 319 | } 320 | 321 | public void addResponseInterceptor(HttpResponseInterceptor responseInterceptor) { 322 | this.responseInterceptors.add(responseInterceptor); 323 | } 324 | } 325 | -------------------------------------------------------------------------------- /src/main/java/com/github/paweladamski/httpclientmock/HttpClientMockBuilder.java: -------------------------------------------------------------------------------- 1 | package com.github.paweladamski.httpclientmock; 2 | 3 | import static org.hamcrest.Matchers.equalTo; 4 | 5 | import com.github.paweladamski.httpclientmock.action.Action; 6 | import com.github.paweladamski.httpclientmock.condition.BodyMatcher; 7 | import com.github.paweladamski.httpclientmock.condition.Condition; 8 | import com.github.paweladamski.httpclientmock.condition.HeaderCondition; 9 | import com.github.paweladamski.httpclientmock.matchers.ParametersMatcher; 10 | import java.io.IOException; 11 | import java.nio.charset.Charset; 12 | import java.nio.charset.StandardCharsets; 13 | import java.util.Collection; 14 | import org.apache.http.NameValuePair; 15 | import org.apache.http.entity.ContentType; 16 | import org.hamcrest.Matcher; 17 | 18 | public class HttpClientMockBuilder { 19 | 20 | private final RuleBuilder ruleBuilder; 21 | private final HttpClientResponseBuilder responseBuilder; 22 | 23 | HttpClientMockBuilder(RuleBuilder rule) { 24 | this.ruleBuilder = rule; 25 | this.responseBuilder = new HttpClientResponseBuilder(rule); 26 | } 27 | 28 | /** 29 | * Adds header condition. Header must be equal to provided value. 30 | * 31 | * @param header header name 32 | * @param value expected value 33 | * @return condition builder 34 | */ 35 | public HttpClientMockBuilder withHeader(String header, String value) { 36 | return withHeader(header, equalTo(value)); 37 | } 38 | 39 | /** 40 | * Adds header condition. Header must be equal to provided value. 41 | * 42 | * @param header header name 43 | * @param matcher header value matcher 44 | * @return condition builder 45 | */ 46 | public HttpClientMockBuilder withHeader(String header, Matcher matcher) { 47 | ruleBuilder.addCondition(new HeaderCondition(header, matcher)); 48 | return this; 49 | } 50 | 51 | /** 52 | * Adds reference condition. Reference must be equal to provided value. 53 | * 54 | * @param reference expected reference 55 | * @return conditions builder 56 | */ 57 | public HttpClientMockBuilder withReference(String reference) { 58 | return withReference(equalTo(reference)); 59 | } 60 | 61 | /** 62 | * Adds reference condition. Reference must match. 63 | * 64 | * @param matcher reference matcher 65 | * @return conditions builder 66 | */ 67 | public HttpClientMockBuilder withReference(Matcher matcher) { 68 | ruleBuilder.addReferenceCondition(matcher); 69 | return this; 70 | } 71 | 72 | /** 73 | * Adds parameter condition. Parameter must be equal to provided value. 74 | * 75 | * @param name parameter name 76 | * @param value expected parameter value 77 | * @return condition builder 78 | */ 79 | public HttpClientMockBuilder withParameter(String name, String value) { 80 | return withParameter(name, equalTo(value)); 81 | } 82 | 83 | /** 84 | * Adds parameter condition. Parameter value must match. 85 | * 86 | * @param name parameter name 87 | * @param matcher parameter value matcher 88 | * @return condition builder 89 | */ 90 | public HttpClientMockBuilder withParameter(String name, Matcher matcher) { 91 | ruleBuilder.addParameterCondition(name, matcher); 92 | return this; 93 | } 94 | 95 | /** 96 | * Request body must contain the given URL-encoded form parameter (typically found in POST requests). Alternatively, parameters may be specified all at once 97 | * using {@link #withFormParameters(ParametersMatcher)}. 98 | * 99 | * @param name parameter name 100 | * @param value expected parameter value 101 | * @return condition builder 102 | */ 103 | public HttpClientMockBuilder withFormParameter(String name, String value) { 104 | return withFormParameter(name, equalTo(value)); 105 | } 106 | 107 | /** 108 | * Request body must contain the given URL-encoded form parameter (typically found in POST requests). Alternatively, parameters may be specified all at once 109 | * using {@link #withFormParameters(ParametersMatcher)}. 110 | * 111 | * @param name parameter name 112 | * @param matcher parameter value matcher 113 | * @return condition builder 114 | */ 115 | public HttpClientMockBuilder withFormParameter(String name, Matcher matcher) { 116 | ruleBuilder.addFormParameterCondition(name, matcher); 117 | return this; 118 | } 119 | 120 | /** 121 | * Request body must contain the given URL-encoded form parameters (typically used in POST requests). Alternatively, parameters may be specified individually 122 | * using {@link #withFormParameter(String, Matcher)}. 123 | * 124 | * @param parameters the parameters 125 | * @return condition builder 126 | */ 127 | public HttpClientMockBuilder withFormParameters(ParametersMatcher parameters) { 128 | ruleBuilder.addFormParameterConditions(parameters); 129 | return this; 130 | } 131 | 132 | /** 133 | * Adds custom conditions. 134 | * 135 | * @param condition custom condition 136 | * @return condition builder 137 | */ 138 | public HttpClientMockBuilder with(Condition condition) { 139 | ruleBuilder.addCondition(condition); 140 | return this; 141 | } 142 | 143 | /** 144 | * Adds body condition. Request body must match provided matcher. 145 | * 146 | * @param matcher custom condition 147 | * @return condition builder 148 | */ 149 | public HttpClientMockBuilder withBody(Matcher matcher) { 150 | ruleBuilder.addCondition(new BodyMatcher(matcher)); 151 | return this; 152 | } 153 | 154 | /** 155 | * Adds host condition. Request host must be equal to provided value. 156 | * 157 | * @param host expected host 158 | * @return condition builder 159 | */ 160 | public HttpClientMockBuilder withHost(String host) { 161 | ruleBuilder.addHostCondition(host); 162 | return this; 163 | } 164 | 165 | /** 166 | * Adds path condition. Request path must be equal to provided value. 167 | * 168 | * @param path expected path 169 | * @return condition builder 170 | */ 171 | public HttpClientMockBuilder withPath(String path) { 172 | return withPath(equalTo(path)); 173 | } 174 | 175 | /** 176 | * Adds path condition. Request path must match. 177 | * 178 | * @param matcher path matcher 179 | * @return condition builder 180 | */ 181 | public HttpClientMockBuilder withPath(Matcher matcher) { 182 | ruleBuilder.addPathCondition(matcher); 183 | return this; 184 | } 185 | 186 | /** 187 | * Allows extra parameters (not defined in condition) in query. 188 | * 189 | * @return condition builder 190 | */ 191 | public HttpClientMockBuilder withExtraParameters() { 192 | ruleBuilder.setAllowExtraParameters(true); 193 | return this; 194 | } 195 | 196 | /** 197 | * Disallows extra parameters (not defined in condition) in query. 198 | * 199 | * @return condition builder 200 | */ 201 | public HttpClientMockBuilder withoutExtraParameters() { 202 | ruleBuilder.setAllowExtraParameters(false); 203 | return this; 204 | } 205 | 206 | /** 207 | * Allows extra parameters (not defined in condition) in form. 208 | * 209 | * @return condition builder 210 | */ 211 | public HttpClientMockBuilder withExtraFormParameters() { 212 | ruleBuilder.setAllowExtraFormParameters(true); 213 | return this; 214 | } 215 | 216 | /** 217 | * Disallows extra parameters (not defined in condition) in form. 218 | * 219 | * @return condition builder 220 | */ 221 | public HttpClientMockBuilder withoutExtraFormParameters() { 222 | ruleBuilder.setAllowExtraFormParameters(false); 223 | return this; 224 | } 225 | 226 | /** 227 | * Adds custom action. 228 | * 229 | * @param action custom action 230 | * @return response builder 231 | */ 232 | public HttpClientResponseBuilder doAction(Action action) { 233 | return responseBuilder.doAction(action); 234 | } 235 | 236 | /** 237 | * Adds action which returns provided response in UTF-8 and status 200. 238 | * 239 | * @param response response to return 240 | * @return response builder 241 | */ 242 | public HttpClientResponseBuilder doReturn(String response) { 243 | return responseBuilder.doReturn(response); 244 | } 245 | 246 | /** 247 | * Adds action which returns provided response and status in UTF-8. 248 | * 249 | * @param statusCode status to return 250 | * @param response response to return 251 | * @return response builder 252 | */ 253 | public HttpClientResponseBuilder doReturn(int statusCode, String response) { 254 | return responseBuilder.doReturn(statusCode, response); 255 | } 256 | 257 | /** 258 | * Adds action which returns provided response in provided charset and status 200. 259 | * 260 | * @param response response to return 261 | * @param charset charset to return 262 | * @return response builder 263 | */ 264 | public HttpClientResponseBuilder doReturn(String response, Charset charset) { 265 | return responseBuilder.doReturn(response, charset); 266 | } 267 | 268 | /** 269 | * Adds action which returns provided response in provided charset and status. 270 | * 271 | * @param statusCode status to return 272 | * @param response response to return 273 | * @param charset the charset 274 | * @return response builder 275 | */ 276 | public HttpClientResponseBuilder doReturn(int statusCode, String response, Charset charset) { 277 | return responseBuilder.doReturn(statusCode, response, charset); 278 | } 279 | 280 | /** 281 | * Adds action which returns provided response in provided charset, content-type and status 200. 282 | * 283 | * @param response response to return 284 | * @return response builder 285 | */ 286 | public HttpClientResponseBuilder doReturn(String response, Charset charset, ContentType contentType) { 287 | return responseBuilder.doReturn(response, charset, contentType); 288 | } 289 | 290 | /** 291 | * Adds action which returns provided status and null entity. 292 | * 293 | * @param statusCode status to return 294 | * @return response builder 295 | */ 296 | public HttpClientResponseBuilder doReturnWithStatus(int statusCode) { 297 | return responseBuilder.doReturnWithStatus(statusCode); 298 | } 299 | 300 | /** 301 | * Adds action which returns provided status with reason and null entity. 302 | * 303 | * @param statusCode status to return 304 | * @param reason reason to return 305 | * @return response builder 306 | */ 307 | public HttpClientResponseBuilder doReturnWithStatus(int statusCode, String reason) { 308 | return responseBuilder.doReturnWithStatus(statusCode, reason); 309 | } 310 | 311 | /** 312 | * Adds action which returns empty message and provided status. 313 | * 314 | * @param statusCode status to return 315 | * @return response builder 316 | * @deprecated use doReturnWithStatus instead 317 | */ 318 | @Deprecated 319 | public HttpClientResponseBuilder doReturnStatus(int statusCode) { 320 | return responseBuilder.doReturnStatus(statusCode); 321 | } 322 | 323 | /** 324 | * Adds action which throws provided exception. 325 | * 326 | * @param exception exception to be thrown 327 | * @return response builder 328 | */ 329 | public HttpClientResponseBuilder doThrowException(IOException exception) { 330 | return responseBuilder.doThrowException(exception); 331 | } 332 | 333 | /** 334 | * Adds action which returns provided JSON in UTF-8 and status 200. Additionally it sets "Content-type" header to "application/json". 335 | * 336 | * @param response JSON to return 337 | * @return response builder 338 | */ 339 | public HttpClientResponseBuilder doReturnJSON(String response) { 340 | return responseBuilder.doReturnJSON(response); 341 | } 342 | 343 | /** 344 | * Adds action which returns provided JSON in provided charset and status 200. Additionally it sets "Content-type" header to "application/json". 345 | * 346 | * @param response JSON to return 347 | * @return response builder 348 | */ 349 | public HttpClientResponseBuilder doReturnJSON(String response, Charset charset) { 350 | return responseBuilder.doReturnJSON(response, charset); 351 | } 352 | 353 | /** 354 | * Adds action which returns provided XML in UTF-8 and status 200. Additionally it sets "Content-type" header to "application/xml". 355 | * 356 | * @param response JSON to return 357 | * @return response builder 358 | */ 359 | public HttpClientResponseBuilder doReturnXML(String response) { 360 | return responseBuilder.doReturnXML(response); 361 | } 362 | 363 | /** 364 | * Adds action which returns provided XML in provided charset and status 200. Additionally it sets "Content-type" header to "application/xml". 365 | * 366 | * @param response JSON to return 367 | * @return response builder 368 | */ 369 | public HttpClientResponseBuilder doReturnXML(String response, Charset charset) { 370 | return responseBuilder.doReturnXML(response, charset); 371 | } 372 | 373 | /** 374 | * Adds action which returns provided URL-encoded parameter response in UTF-8 and status 200. Additionally it sets "Content-type" header to 375 | * "application/x-www-form-urlencoded". 376 | * 377 | * @param parameters parameters to return 378 | * @return response builder 379 | */ 380 | public HttpClientResponseBuilder doReturnFormParams(Collection parameters) { 381 | return doReturnFormParams(parameters, StandardCharsets.UTF_8); 382 | } 383 | 384 | /** 385 | * Adds action which returns provided URL-encoded parameter response in provided charset and status 200. Additionally it sets "Content-type" header to 386 | * "application/x-www-form-urlencoded". 387 | * 388 | * @param parameters parameters to return 389 | * @return response builder 390 | */ 391 | public HttpClientResponseBuilder doReturnFormParams(Collection parameters, Charset charset) { 392 | return responseBuilder.doReturnFormParams(parameters, charset); 393 | } 394 | 395 | } 396 | -------------------------------------------------------------------------------- /src/main/java/com/github/paweladamski/httpclientmock/HttpClientResponseBuilder.java: -------------------------------------------------------------------------------- 1 | package com.github.paweladamski.httpclientmock; 2 | 3 | import static org.apache.http.entity.ContentType.APPLICATION_JSON; 4 | import static org.apache.http.entity.ContentType.APPLICATION_XML; 5 | 6 | import com.github.paweladamski.httpclientmock.action.Action; 7 | import com.github.paweladamski.httpclientmock.action.CookieAction; 8 | import com.github.paweladamski.httpclientmock.action.ExceptionAction; 9 | import com.github.paweladamski.httpclientmock.action.HeaderAction; 10 | import com.github.paweladamski.httpclientmock.action.StatusWithEmptyEntityResponse; 11 | import com.github.paweladamski.httpclientmock.action.StatusWithNullEntityResponse; 12 | import com.github.paweladamski.httpclientmock.action.StringResponse; 13 | import com.github.paweladamski.httpclientmock.action.UrlEncodedFormEntityResponse; 14 | import java.io.IOException; 15 | import java.nio.charset.Charset; 16 | import java.nio.charset.StandardCharsets; 17 | import java.util.Collection; 18 | import org.apache.http.NameValuePair; 19 | import org.apache.http.entity.ContentType; 20 | 21 | public class HttpClientResponseBuilder { 22 | 23 | private final RuleBuilder newRule; 24 | 25 | HttpClientResponseBuilder(RuleBuilder rule) { 26 | this.newRule = rule; 27 | } 28 | 29 | /** 30 | * Sets response header. 31 | * 32 | * @param name header name 33 | * @param value header value 34 | * @return response builder 35 | */ 36 | public HttpClientResponseBuilder withHeader(String name, String value) { 37 | Action lastAction = newRule.getLastAction(); 38 | HeaderAction headerAction = new HeaderAction(lastAction, name, value); 39 | newRule.overrideLastAction(headerAction); 40 | return this; 41 | } 42 | 43 | /** 44 | * Sets response status code. 45 | * 46 | * @param statusCode response status code 47 | * @return response builder 48 | */ 49 | public HttpClientResponseBuilder withStatus(int statusCode) { 50 | Action lastAction = newRule.getLastAction(); 51 | StatusWithEmptyEntityResponse statusAction = new StatusWithEmptyEntityResponse(lastAction, statusCode); 52 | newRule.overrideLastAction(statusAction); 53 | return this; 54 | } 55 | 56 | /** 57 | * Sets response status code and status reason. 58 | * 59 | * @param statusCode response status code 60 | * @param statusReason response status reason 61 | * @return response builder 62 | */ 63 | public HttpClientResponseBuilder withStatus(int statusCode, String statusReason) { 64 | Action lastAction = newRule.getLastAction(); 65 | StatusWithEmptyEntityResponse statusAction = new StatusWithEmptyEntityResponse(lastAction, statusCode, statusReason); 66 | newRule.overrideLastAction(statusAction); 67 | return this; 68 | } 69 | 70 | /** 71 | * Sets response cookie 72 | * 73 | * @param cookieName cookie name 74 | * @param cookieValue cookie value 75 | * @return response builder 76 | */ 77 | public HttpClientResponseBuilder withCookie(String cookieName, String cookieValue) { 78 | Action lastAction = newRule.getLastAction(); 79 | CookieAction cookieAction = new CookieAction(lastAction, cookieName, cookieValue); 80 | newRule.overrideLastAction(cookieAction); 81 | return this; 82 | } 83 | 84 | /** 85 | * Adds custom action. 86 | * 87 | * @param action custom action 88 | * @return response builder 89 | */ 90 | public HttpClientResponseBuilder doAction(Action action) { 91 | newRule.addAction(action); 92 | return new HttpClientResponseBuilder(newRule); 93 | } 94 | 95 | /** 96 | * Adds action which returns provided response in UTF-8 and status 200. 97 | * 98 | * @param response response to return 99 | * @return response builder 100 | */ 101 | public HttpClientResponseBuilder doReturn(String response) { 102 | return doReturn(response, StandardCharsets.UTF_8); 103 | } 104 | 105 | /** 106 | * Adds action which returns provided response in UTF-8 with status code. 107 | * 108 | * @param statusCode status to return 109 | * @param response response to return 110 | * @return response builder 111 | */ 112 | public HttpClientResponseBuilder doReturn(int statusCode, String response) { 113 | return doReturn(statusCode, response, StandardCharsets.UTF_8); 114 | } 115 | 116 | /** 117 | * Adds action which returns provided response in provided charset and status 200. 118 | * 119 | * @param response response to return 120 | * @return response builder 121 | */ 122 | public HttpClientResponseBuilder doReturn(String response, Charset charset) { 123 | newRule.addAction(new StringResponse(response, charset)); 124 | return new HttpClientResponseBuilder(newRule); 125 | } 126 | 127 | /** 128 | * Adds action which returns provided response in provided charset, content type and status 200. 129 | * 130 | * @param response response to return 131 | * @return response builder 132 | */ 133 | public HttpClientResponseBuilder doReturn(String response, Charset charset, ContentType contentType) { 134 | newRule.addAction(new StringResponse(response, charset, contentType)); 135 | return new HttpClientResponseBuilder(newRule); 136 | } 137 | 138 | /** 139 | * Adds action which returns provided response in provided charset and status code. 140 | * 141 | * @param statusCode status to return 142 | * @param response response to return 143 | * @param charset the charset 144 | * @return response builder 145 | */ 146 | public HttpClientResponseBuilder doReturn(int statusCode, String response, Charset charset) { 147 | newRule.addAction(new StringResponse(statusCode, response, charset)); 148 | return new HttpClientResponseBuilder(newRule); 149 | } 150 | 151 | /** 152 | * Adds action which returns provided status and null entity. 153 | * 154 | * @param statusCode status to return 155 | * @return response builder 156 | */ 157 | public HttpClientResponseBuilder doReturnWithStatus(int statusCode) { 158 | newRule.addAction(new StatusWithNullEntityResponse(statusCode)); 159 | return new HttpClientResponseBuilder(newRule); 160 | } 161 | 162 | /** 163 | * Adds action which returns provided status with reason and null entity. 164 | * 165 | * @param statusCode status to return 166 | * @param reason reason to return 167 | * @return response builder 168 | */ 169 | public HttpClientResponseBuilder doReturnWithStatus(int statusCode, String reason) { 170 | newRule.addAction(new StatusWithNullEntityResponse(statusCode, reason)); 171 | return new HttpClientResponseBuilder(newRule); 172 | } 173 | 174 | 175 | /** 176 | * Adds action which returns empty message and provided status. 177 | * 178 | * @param statusCode status to return 179 | * @return response builder 180 | * @deprecated use doReturnWithStatus instead 181 | */ 182 | @Deprecated 183 | public HttpClientResponseBuilder doReturnStatus(int statusCode) { 184 | newRule.addAction(new StatusWithEmptyEntityResponse(statusCode)); 185 | return new HttpClientResponseBuilder(newRule); 186 | } 187 | 188 | /** 189 | * Adds action which throws provided exception. 190 | * 191 | * @param exception exception to be thrown 192 | * @return response builder 193 | */ 194 | public HttpClientResponseBuilder doThrowException(IOException exception) { 195 | newRule.addAction(new ExceptionAction(exception)); 196 | return new HttpClientResponseBuilder(newRule); 197 | } 198 | 199 | /** 200 | * Adds action which returns provided JSON in UTF-8 and status 200. Additionally it sets "Content-type" header to "application/json". 201 | * 202 | * @param response JSON to return 203 | * @return response builder 204 | */ 205 | public HttpClientResponseBuilder doReturnJSON(String response) { 206 | return doReturnJSON(response, StandardCharsets.UTF_8); 207 | } 208 | 209 | /** 210 | * Adds action which returns provided JSON in provided encoding and status 200. Additionally it sets "Content-type" header to "application/json". 211 | * 212 | * @param response JSON to return 213 | * @return response builder 214 | */ 215 | public HttpClientResponseBuilder doReturnJSON(String response, Charset charset) { 216 | return doReturn(response, charset, APPLICATION_JSON); 217 | } 218 | 219 | /** 220 | * Adds action which returns provided XML in UTF-8 and status 200. Additionally it sets "Content-type" header to "application/xml". 221 | * 222 | * @param response JSON to return 223 | * @return response builder 224 | */ 225 | public HttpClientResponseBuilder doReturnXML(String response) { 226 | return doReturnXML(response, StandardCharsets.UTF_8); 227 | } 228 | 229 | /** 230 | * Adds action which returns provided XML in UTF-8 and status 200. Additionally it sets "Content-type" header to "application/xml". 231 | * 232 | * @param response JSON to return 233 | * @return response builder 234 | */ 235 | public HttpClientResponseBuilder doReturnXML(String response, Charset charset) { 236 | return doReturn(response, charset, APPLICATION_XML); 237 | } 238 | 239 | /** 240 | * Adds action which returns provided name/value pairs as URL-encoded form response in UTF-8 and status 200. Additionally it sets "Content-type" header to 241 | * "application/x-www-form-urlencoded". 242 | * 243 | * @param formParameters the parameters to include in the response 244 | * @return response builder 245 | */ 246 | public HttpClientResponseBuilder doReturnFormParams(Collection formParameters) { 247 | return doReturnFormParams(formParameters, StandardCharsets.UTF_8); 248 | } 249 | 250 | /** 251 | * Adds action which returns provided name/value pairs as URL-encoded form response and status 200. Additionally it sets "Content-type" header to 252 | * "application/x-www-form-urlencoded". 253 | * 254 | * @param formParameters the parameters to include in the response 255 | * @return response builder 256 | */ 257 | public HttpClientResponseBuilder doReturnFormParams(Collection formParameters, Charset charset) { 258 | newRule.addAction(new UrlEncodedFormEntityResponse(formParameters, charset)); 259 | return new HttpClientResponseBuilder(newRule); 260 | } 261 | 262 | } 263 | -------------------------------------------------------------------------------- /src/main/java/com/github/paweladamski/httpclientmock/HttpClientVerify.java: -------------------------------------------------------------------------------- 1 | package com.github.paweladamski.httpclientmock; 2 | 3 | import java.util.List; 4 | 5 | import org.apache.http.client.methods.HttpDelete; 6 | import org.apache.http.client.methods.HttpGet; 7 | import org.apache.http.client.methods.HttpHead; 8 | import org.apache.http.client.methods.HttpOptions; 9 | import org.apache.http.client.methods.HttpPatch; 10 | import org.apache.http.client.methods.HttpPost; 11 | import org.apache.http.client.methods.HttpPut; 12 | 13 | public class HttpClientVerify { 14 | 15 | private final String defaultHost; 16 | private final List requests; 17 | 18 | public HttpClientVerify(String defaultHost, List requests) { 19 | this.requests = requests; 20 | this.defaultHost = defaultHost; 21 | } 22 | 23 | private HttpClientVerifyBuilder newRule(String method) { 24 | RuleBuilder r = new RuleBuilder(method); 25 | return new HttpClientVerifyBuilder(r, requests); 26 | } 27 | 28 | private HttpClientVerifyBuilder newRule(String method, String url) { 29 | RuleBuilder r = new RuleBuilder(method, defaultHost, url); 30 | return new HttpClientVerifyBuilder(r, requests); 31 | } 32 | 33 | public HttpClientVerifyBuilder post(String url) { 34 | return newRule(HttpPost.METHOD_NAME, url); 35 | } 36 | 37 | public HttpClientVerifyBuilder get(String url) { 38 | return newRule(HttpGet.METHOD_NAME, url); 39 | } 40 | 41 | public HttpClientVerifyBuilder put(String url) { 42 | return newRule(HttpPut.METHOD_NAME, url); 43 | } 44 | 45 | public HttpClientVerifyBuilder delete(String url) { 46 | return newRule(HttpDelete.METHOD_NAME, url); 47 | } 48 | 49 | public HttpClientVerifyBuilder head(String url) { 50 | return newRule(HttpHead.METHOD_NAME, url); 51 | } 52 | 53 | public HttpClientVerifyBuilder options(String url) { 54 | return newRule(HttpOptions.METHOD_NAME, url); 55 | } 56 | 57 | public HttpClientVerifyBuilder patch(String url) { 58 | return newRule(HttpPatch.METHOD_NAME, url); 59 | } 60 | 61 | public HttpClientVerifyBuilder post() { 62 | return newRule(HttpPost.METHOD_NAME); 63 | } 64 | 65 | public HttpClientVerifyBuilder get() { 66 | return newRule(HttpGet.METHOD_NAME); 67 | } 68 | 69 | public HttpClientVerifyBuilder put() { 70 | return newRule(HttpPut.METHOD_NAME); 71 | } 72 | 73 | public HttpClientVerifyBuilder delete() { 74 | return newRule(HttpDelete.METHOD_NAME); 75 | } 76 | 77 | public HttpClientVerifyBuilder head() { 78 | return newRule(HttpHead.METHOD_NAME); 79 | } 80 | 81 | public HttpClientVerifyBuilder options() { 82 | return newRule(HttpOptions.METHOD_NAME); 83 | } 84 | 85 | public HttpClientVerifyBuilder patch() { 86 | return newRule(HttpPatch.METHOD_NAME); 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/com/github/paweladamski/httpclientmock/HttpClientVerifyBuilder.java: -------------------------------------------------------------------------------- 1 | package com.github.paweladamski.httpclientmock; 2 | 3 | import static org.hamcrest.Matchers.equalTo; 4 | 5 | import com.github.paweladamski.httpclientmock.condition.BodyMatcher; 6 | import com.github.paweladamski.httpclientmock.condition.Condition; 7 | import com.github.paweladamski.httpclientmock.condition.HeaderCondition; 8 | import com.github.paweladamski.httpclientmock.matchers.ParametersMatcher; 9 | import java.util.List; 10 | import org.hamcrest.Matcher; 11 | 12 | public class HttpClientVerifyBuilder { 13 | 14 | private final RuleBuilder ruleBuilder; 15 | private final List requests; 16 | 17 | HttpClientVerifyBuilder(RuleBuilder ruleBuilder, List requests) { 18 | this.requests = requests; 19 | this.ruleBuilder = ruleBuilder; 20 | } 21 | 22 | /** 23 | * Adds header condition. Header must be equal to provided value. 24 | * 25 | * @param header header name 26 | * @param value expected value 27 | * @return verification builder 28 | */ 29 | public HttpClientVerifyBuilder withHeader(String header, String value) { 30 | return withHeader(header, equalTo(value)); 31 | } 32 | 33 | /** 34 | * Adds header condition. Header must be equal to provided value. 35 | * 36 | * @param header header name 37 | * @param matcher header value matcher 38 | * @return verification builder 39 | */ 40 | public HttpClientVerifyBuilder withHeader(String header, Matcher matcher) { 41 | ruleBuilder.addCondition(new HeaderCondition(header, matcher)); 42 | return this; 43 | } 44 | 45 | /** 46 | * Adds reference condition. Reference must be equal to provided value. 47 | * 48 | * @param reference expected reference 49 | * @return conditions builder 50 | */ 51 | public HttpClientVerifyBuilder withReference(String reference) { 52 | return withReference(equalTo(reference)); 53 | } 54 | 55 | /** 56 | * Adds reference condition. Reference must match. 57 | * 58 | * @param matcher reference matcher 59 | * @return conditions builder 60 | */ 61 | public HttpClientVerifyBuilder withReference(Matcher matcher) { 62 | ruleBuilder.addReferenceCondition(matcher); 63 | return this; 64 | } 65 | 66 | /** 67 | * Adds parameter condition. Parameter must be equal to provided value. 68 | * 69 | * @param name parameter name 70 | * @param value expected parameter value 71 | * @return verification builder 72 | */ 73 | public HttpClientVerifyBuilder withParameter(String name, String value) { 74 | return withParameter(name, equalTo(value)); 75 | } 76 | 77 | /** 78 | * Adds parameter condition. Parameter value must match. 79 | * 80 | * @param name parameter name 81 | * @param matcher parameter value matcher 82 | * @return verification builder 83 | */ 84 | public HttpClientVerifyBuilder withParameter(String name, Matcher matcher) { 85 | ruleBuilder.addParameterCondition(name, matcher); 86 | return this; 87 | } 88 | 89 | /** 90 | * Request body must contain the given URL-encoded form parameter (typically found in POST requests). Alternatively, parameters may be specified all at once 91 | * using {@link #withFormParameters(ParametersMatcher)}. 92 | * 93 | * @param name parameter name 94 | * @param value expected parameter value 95 | * @return verification builder 96 | */ 97 | public HttpClientVerifyBuilder withFormParameter(String name, String value) { 98 | return withFormParameter(name, equalTo(value)); 99 | } 100 | 101 | /** 102 | * Request body must contain the given URL-encoded form parameter (typically found in POST requests). Alternatively, parameters may be specified all at once 103 | * using {@link #withFormParameters(ParametersMatcher)}. 104 | * 105 | * @param name parameter name 106 | * @param matcher parameter value matcher 107 | * @return verification builder 108 | */ 109 | public HttpClientVerifyBuilder withFormParameter(String name, Matcher matcher) { 110 | ruleBuilder.addFormParameterCondition(name, matcher); 111 | return this; 112 | } 113 | 114 | /** 115 | * Request body must contain the given URL-encoded form parameters (typically used in POST requests). Alternatively, parameters may be specified individually 116 | * using {@link #withFormParameter(String, Matcher)}. 117 | * 118 | * @param parameters the parameters 119 | * @return condition builder 120 | */ 121 | public HttpClientVerifyBuilder withFormParameters(ParametersMatcher parameters) { 122 | ruleBuilder.addFormParameterConditions(parameters); 123 | return this; 124 | } 125 | 126 | /** 127 | * Adds custom conditions. 128 | * 129 | * @param condition custom condition 130 | * @return verification builder 131 | */ 132 | public HttpClientVerifyBuilder with(Condition condition) { 133 | ruleBuilder.addCondition(condition); 134 | return this; 135 | } 136 | 137 | /** 138 | * Adds body condition. Request body must match provided matcher. 139 | * 140 | * @param matcher custom condition 141 | * @return verification builder 142 | */ 143 | public HttpClientVerifyBuilder withBody(Matcher matcher) { 144 | ruleBuilder.addCondition(new BodyMatcher(matcher)); 145 | return this; 146 | } 147 | 148 | /** 149 | * Adds host condition. Request host must be equal to provided value. 150 | * 151 | * @param host expected host 152 | * @return verification builder 153 | */ 154 | public HttpClientVerifyBuilder withHost(String host) { 155 | ruleBuilder.addHostCondition(host); 156 | return this; 157 | } 158 | 159 | /** 160 | * Adds path condition. Request path must be equal to provided value. 161 | * 162 | * @param path expected path 163 | * @return verification builder 164 | */ 165 | public HttpClientVerifyBuilder withPath(String path) { 166 | return withPath(equalTo(path)); 167 | } 168 | 169 | /** 170 | * Adds path condition. Request path must match. 171 | * 172 | * @param matcher path matcher 173 | * @return verification builder 174 | */ 175 | public HttpClientVerifyBuilder withPath(Matcher matcher) { 176 | ruleBuilder.addPathCondition(matcher); 177 | return this; 178 | } 179 | 180 | /** 181 | * Allows extra parameters (not defined in condition) in query. 182 | * 183 | * @return verification builder 184 | */ 185 | public HttpClientVerifyBuilder withExtraParameters() { 186 | ruleBuilder.setAllowExtraParameters(true); 187 | return this; 188 | } 189 | 190 | /** 191 | * Disallows extra parameters (not defined in condition) in query. 192 | * 193 | * @return condition builder 194 | */ 195 | public HttpClientVerifyBuilder withoutExtraParameters() { 196 | ruleBuilder.setAllowExtraParameters(false); 197 | return this; 198 | } 199 | 200 | /** 201 | * Allows extra parameters (not defined in condition) in form. 202 | * 203 | * @return verification builder 204 | */ 205 | public HttpClientVerifyBuilder withExtraFormParameters() { 206 | ruleBuilder.setAllowExtraFormParameters(true); 207 | return this; 208 | } 209 | 210 | /** 211 | * Disallows extra parameters (not defined in condition) in form. 212 | * 213 | * @return verification builder 214 | */ 215 | public HttpClientVerifyBuilder withoutExtraFormParameters() { 216 | ruleBuilder.setAllowExtraFormParameters(false); 217 | return this; 218 | } 219 | 220 | /** 221 | * Verifies if there were no request matching defined conditions. 222 | */ 223 | public void notCalled() { 224 | called(0); 225 | } 226 | 227 | /** 228 | * Verifies if there was exactly one request matching defined conditions. 229 | */ 230 | public void called() { 231 | called(1); 232 | } 233 | 234 | /** 235 | * Verifies number of request matching defined conditions. 236 | * 237 | * @param numberOfCalls expected number of calls 238 | */ 239 | public void called(int numberOfCalls) { 240 | called(equalTo(numberOfCalls)); 241 | } 242 | 243 | /** 244 | * Verifies number of request matching defined conditions. 245 | * 246 | * @param numberOfCalls expected number of calls 247 | */ 248 | public void called(Matcher numberOfCalls) { 249 | Rule rule = ruleBuilder.toRule(); 250 | int matchingCalls = (int) requests.stream() 251 | .filter(rule::matches) 252 | .count(); 253 | 254 | if (!numberOfCalls.matches(matchingCalls)) { 255 | throw new IllegalStateException(String.format("Expected %s calls, but found %s.", numberOfCalls, matchingCalls)); 256 | } 257 | } 258 | 259 | } 260 | -------------------------------------------------------------------------------- /src/main/java/com/github/paweladamski/httpclientmock/HttpResponseProxy.java: -------------------------------------------------------------------------------- 1 | package com.github.paweladamski.httpclientmock; 2 | 3 | import java.io.IOException; 4 | import java.util.Locale; 5 | import org.apache.http.Header; 6 | import org.apache.http.HeaderIterator; 7 | import org.apache.http.HttpEntity; 8 | import org.apache.http.HttpResponse; 9 | import org.apache.http.ProtocolVersion; 10 | import org.apache.http.StatusLine; 11 | import org.apache.http.client.methods.CloseableHttpResponse; 12 | import org.apache.http.params.HttpParams; 13 | 14 | class HttpResponseProxy implements CloseableHttpResponse { 15 | 16 | private final HttpResponse original; 17 | 18 | public HttpResponseProxy(HttpResponse original) { 19 | this.original = original; 20 | 21 | } 22 | 23 | public void close() throws IOException { 24 | 25 | } 26 | 27 | public StatusLine getStatusLine() { 28 | return this.original.getStatusLine(); 29 | } 30 | 31 | public void setStatusLine(StatusLine statusline) { 32 | this.original.setStatusLine(statusline); 33 | } 34 | 35 | public void setStatusLine(ProtocolVersion ver, int code) { 36 | this.original.setStatusLine(ver, code); 37 | } 38 | 39 | public void setStatusLine(ProtocolVersion ver, int code, String reason) { 40 | this.original.setStatusLine(ver, code, reason); 41 | } 42 | 43 | public void setStatusCode(int code) throws IllegalStateException { 44 | this.original.setStatusCode(code); 45 | } 46 | 47 | public void setReasonPhrase(String reason) throws IllegalStateException { 48 | this.original.setReasonPhrase(reason); 49 | } 50 | 51 | public HttpEntity getEntity() { 52 | return this.original.getEntity(); 53 | } 54 | 55 | public void setEntity(HttpEntity entity) { 56 | this.original.setEntity(entity); 57 | } 58 | 59 | public Locale getLocale() { 60 | return this.original.getLocale(); 61 | } 62 | 63 | public void setLocale(Locale loc) { 64 | this.original.setLocale(loc); 65 | } 66 | 67 | public ProtocolVersion getProtocolVersion() { 68 | return this.original.getProtocolVersion(); 69 | } 70 | 71 | public boolean containsHeader(String name) { 72 | return this.original.containsHeader(name); 73 | } 74 | 75 | public Header[] getHeaders(String name) { 76 | return this.original.getHeaders(name); 77 | } 78 | 79 | public Header getFirstHeader(String name) { 80 | return this.original.getFirstHeader(name); 81 | } 82 | 83 | public Header getLastHeader(String name) { 84 | return this.original.getLastHeader(name); 85 | } 86 | 87 | public Header[] getAllHeaders() { 88 | return this.original.getAllHeaders(); 89 | } 90 | 91 | public void addHeader(Header header) { 92 | this.original.addHeader(header); 93 | } 94 | 95 | public void addHeader(String name, String value) { 96 | this.original.addHeader(name, value); 97 | } 98 | 99 | public void setHeader(Header header) { 100 | this.original.setHeader(header); 101 | } 102 | 103 | public void setHeader(String name, String value) { 104 | this.original.setHeader(name, value); 105 | } 106 | 107 | public void setHeaders(Header[] headers) { 108 | this.original.setHeaders(headers); 109 | } 110 | 111 | public void removeHeader(Header header) { 112 | this.original.removeHeader(header); 113 | } 114 | 115 | public void removeHeaders(String name) { 116 | this.original.removeHeaders(name); 117 | } 118 | 119 | public HeaderIterator headerIterator() { 120 | return this.original.headerIterator(); 121 | } 122 | 123 | public HeaderIterator headerIterator(String name) { 124 | return this.original.headerIterator(name); 125 | } 126 | 127 | /** 128 | * @deprecated 129 | */ 130 | @Deprecated 131 | public HttpParams getParams() { 132 | return this.original.getParams(); 133 | } 134 | 135 | /** 136 | * @deprecated 137 | */ 138 | @Deprecated 139 | public void setParams(HttpParams params) { 140 | this.original.setParams(params); 141 | } 142 | 143 | public String toString() { 144 | return "HttpResponseProxy{" + this.original + '}'; 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /src/main/java/com/github/paweladamski/httpclientmock/Request.java: -------------------------------------------------------------------------------- 1 | package com.github.paweladamski.httpclientmock; 2 | 3 | import java.net.URI; 4 | import org.apache.http.HttpHost; 5 | import org.apache.http.HttpRequest; 6 | import org.apache.http.protocol.HttpContext; 7 | 8 | public class Request { 9 | 10 | private final HttpHost httpHost; 11 | private final HttpRequest httpRequest; 12 | private final HttpContext httpContext; 13 | 14 | public Request(HttpHost httpHost, HttpRequest httpRequest, HttpContext httpContext) { 15 | 16 | this.httpHost = httpHost; 17 | this.httpRequest = httpRequest; 18 | this.httpContext = httpContext; 19 | } 20 | 21 | public HttpHost getHttpHost() { 22 | return httpHost; 23 | } 24 | 25 | public HttpRequest getHttpRequest() { 26 | return httpRequest; 27 | } 28 | 29 | public HttpContext getHttpContext() { 30 | return httpContext; 31 | } 32 | 33 | public String getUri() { 34 | URI uri = URI.create(httpRequest.getRequestLine().getUri()); 35 | String urlText; 36 | if (uri.isAbsolute()) { 37 | urlText = uri.toString(); 38 | } else { 39 | urlText = httpHost.toString() + uri.toString(); 40 | } 41 | return urlText; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/github/paweladamski/httpclientmock/Rule.java: -------------------------------------------------------------------------------- 1 | package com.github.paweladamski.httpclientmock; 2 | 3 | import static java.util.Collections.emptyList; 4 | import static org.apache.http.HttpStatus.SC_NOT_FOUND; 5 | 6 | import com.github.paweladamski.httpclientmock.action.Action; 7 | import com.github.paweladamski.httpclientmock.action.StatusWithEmptyEntityResponse; 8 | import com.github.paweladamski.httpclientmock.condition.Condition; 9 | import java.io.IOException; 10 | import java.util.ArrayList; 11 | import java.util.LinkedList; 12 | import java.util.List; 13 | import org.apache.http.HttpHost; 14 | import org.apache.http.HttpRequest; 15 | import org.apache.http.HttpResponse; 16 | import org.apache.http.protocol.HttpContext; 17 | 18 | public class Rule { 19 | 20 | public static final Rule NOT_FOUND = new Rule(new UrlConditions(), emptyList(), notFoundAction()); 21 | private final LinkedList actions; 22 | private final List conditions; 23 | private final UrlConditions urlConditions; 24 | 25 | public Rule(UrlConditions urlConditions, List conditions, List actions) { 26 | this.urlConditions = urlConditions; 27 | this.conditions = conditions; 28 | this.actions = new LinkedList<>(actions); 29 | } 30 | 31 | boolean matches(Request request) { 32 | return urlConditions.matches(request.getUri()) 33 | && conditions.stream() 34 | .allMatch(c -> c.matches(request)); 35 | } 36 | 37 | boolean matches(HttpHost httpHost, HttpRequest httpRequest, HttpContext httpContext) { 38 | return matches(new Request(httpHost, httpRequest, httpContext)); 39 | } 40 | 41 | HttpResponse nextResponse(Request request) throws IOException { 42 | Action action = (actions.size() > 1) ? actions.poll() : actions.peek(); 43 | return action.getResponse(request); 44 | } 45 | 46 | public void debug(Request request, Debugger debugger) { 47 | for (Condition condition : conditions) { 48 | condition.debug(request, debugger); 49 | } 50 | urlConditions.debug(request, debugger); 51 | } 52 | 53 | private static List notFoundAction() { 54 | ArrayList actions = new ArrayList<>(); 55 | actions.add(new StatusWithEmptyEntityResponse(SC_NOT_FOUND)); 56 | return actions; 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/github/paweladamski/httpclientmock/RuleBuilder.java: -------------------------------------------------------------------------------- 1 | package com.github.paweladamski.httpclientmock; 2 | 3 | import com.github.paweladamski.httpclientmock.action.Action; 4 | import com.github.paweladamski.httpclientmock.condition.Condition; 5 | import com.github.paweladamski.httpclientmock.condition.HttpMethodCondition; 6 | import com.github.paweladamski.httpclientmock.condition.UrlEncodedFormCondition; 7 | import com.github.paweladamski.httpclientmock.matchers.ParametersMatcher; 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | import org.hamcrest.Matcher; 11 | 12 | class RuleBuilder { 13 | 14 | private final List actions = new ArrayList<>(); 15 | private final List conditions = new ArrayList<>(); 16 | private final UrlEncodedFormCondition formParametersCondition = new UrlEncodedFormCondition(); 17 | private final UrlConditions urlConditions; 18 | 19 | RuleBuilder(String method, String defaultHost, String url) { 20 | this.urlConditions = new UrlParser().parse(buildFinalUrl(defaultHost, url)); 21 | addCondition(new HttpMethodCondition(method)); 22 | addCondition(formParametersCondition); 23 | setAllowExtraParameters(true); 24 | setAllowExtraFormParameters(true); 25 | } 26 | 27 | RuleBuilder(String method) { 28 | this.urlConditions = new UrlConditions(); 29 | addCondition(new HttpMethodCondition(method)); 30 | addCondition(formParametersCondition); 31 | setAllowExtraParameters(true); 32 | setAllowExtraFormParameters(true); 33 | } 34 | 35 | private String buildFinalUrl(String defaultHost, String url) { 36 | if (url.startsWith("/")) { 37 | return defaultHost + url; 38 | } else { 39 | return url; 40 | } 41 | } 42 | 43 | void addAction(Action o) { 44 | actions.add(o); 45 | } 46 | 47 | void addCondition(Condition o) { 48 | conditions.add(o); 49 | } 50 | 51 | void addParameterCondition(String name, Matcher matcher) { 52 | urlConditions.getUrlQueryConditions().put(name, matcher); 53 | } 54 | 55 | void addFormParameterCondition(String name, Matcher matcher) { 56 | formParametersCondition.addExpectedParameter(name, matcher); 57 | } 58 | 59 | void addFormParameterConditions(ParametersMatcher parameters) { 60 | formParametersCondition.addExpectedParameters(parameters); 61 | } 62 | 63 | void addReferenceCondition(Matcher matcher) { 64 | urlConditions.setReferenceConditions(matcher); 65 | } 66 | 67 | void addHostCondition(String host) { 68 | UrlParser urlParser = new UrlParser(); 69 | urlConditions.setHostConditions(urlParser.parse(host).getHostConditions()); 70 | } 71 | 72 | void addPathCondition(Matcher matcher) { 73 | urlConditions.getPathConditions().add(matcher); 74 | } 75 | 76 | Action getLastAction() { 77 | return actions.get(actions.size() - 1); 78 | } 79 | 80 | void overrideLastAction(Action lastAction) { 81 | actions.set(actions.size() - 1, lastAction); 82 | } 83 | 84 | Rule toRule() { 85 | return new Rule(urlConditions, conditions, actions); 86 | } 87 | 88 | public void setAllowExtraParameters(boolean allowExtraParameters) { 89 | urlConditions.getUrlQueryConditions().setAllowExtraParameters(allowExtraParameters); 90 | } 91 | 92 | public void setAllowExtraFormParameters(boolean allowExtraFormParameters) { 93 | formParametersCondition.setAllowExtraParameters(allowExtraFormParameters); 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/com/github/paweladamski/httpclientmock/UrlConditions.java: -------------------------------------------------------------------------------- 1 | package com.github.paweladamski.httpclientmock; 2 | 3 | import static org.hamcrest.Matchers.isEmptyOrNullString; 4 | 5 | import com.github.paweladamski.httpclientmock.matchers.MatchersList; 6 | import com.github.paweladamski.httpclientmock.matchers.UrlQueryMatcher; 7 | import java.net.MalformedURLException; 8 | import java.net.URL; 9 | import org.hamcrest.Matcher; 10 | import org.hamcrest.Matchers; 11 | import org.hamcrest.StringDescription; 12 | 13 | public class UrlConditions { 14 | 15 | private static final int EMPTY_PORT = -1; 16 | private UrlQueryMatcher urlQueryConditions = new UrlQueryMatcher(); 17 | private Matcher referenceConditions = Matchers.isEmptyOrNullString(); 18 | private MatchersList hostConditions = new MatchersList<>(); 19 | private MatchersList pathConditions = new MatchersList<>(); 20 | private MatchersList portConditions = new MatchersList<>(); 21 | private Matcher schemaConditions = Matchers.any(String.class); 22 | 23 | public UrlQueryMatcher getUrlQueryConditions() { 24 | return urlQueryConditions; 25 | } 26 | 27 | public Matcher getReferenceConditions() { 28 | return referenceConditions; 29 | } 30 | 31 | public void setReferenceConditions(Matcher referenceConditions) { 32 | this.referenceConditions = referenceConditions; 33 | } 34 | 35 | public MatchersList getHostConditions() { 36 | return hostConditions; 37 | } 38 | 39 | public void setHostConditions(MatchersList hostConditions) { 40 | this.hostConditions = hostConditions; 41 | } 42 | 43 | public MatchersList getPathConditions() { 44 | return pathConditions; 45 | } 46 | 47 | public MatchersList getPortConditions() { 48 | return portConditions; 49 | } 50 | 51 | public void setSchemaConditions(Matcher schemaConditions) { 52 | this.schemaConditions = schemaConditions; 53 | } 54 | 55 | boolean matches(String urlText) { 56 | try { 57 | URL url = new URL(urlText); 58 | 59 | return hostConditions.allMatches(url.getHost()) 60 | && pathConditions.allMatches(url.getPath()) 61 | && portConditions.allMatches(url.getPort()) 62 | && referenceConditions.matches(url.getRef()) 63 | && schemaConditions.matches(url.getProtocol()) 64 | && urlQueryConditions.matches(url.getQuery()); 65 | 66 | } catch (MalformedURLException e) { 67 | return false; 68 | } 69 | } 70 | 71 | 72 | void debug(Request request, Debugger debugger) { 73 | try { 74 | URL url = new URL(request.getUri()); 75 | debugger.message(hostConditions.allMatches(url.getHost()), "schema is " + describe(schemaConditions)); 76 | debugger.message(hostConditions.allMatches(url.getHost()), "host is " + hostConditions.describe()); 77 | debugger.message(pathConditions.allMatches(url.getPath()), "path is " + pathConditions.describe()); 78 | debugger.message(portConditions.allMatches(url.getPort()), "port is " + portDebugDescription()); 79 | if (referenceConditions != isEmptyOrNullString() || !referenceConditions.matches(url.getRef())) { 80 | debugger.message(referenceConditions.matches(url.getRef()), "reference is " + describe(referenceConditions)); 81 | } 82 | urlQueryConditions.describe(url.getQuery(), debugger); 83 | } catch (MalformedURLException e) { 84 | System.out.println("Can't parse URL: " + request.getUri()); 85 | } 86 | } 87 | 88 | private String describe(Matcher matcher) { 89 | return StringDescription.toString(matcher); 90 | } 91 | 92 | private String portDebugDescription() { 93 | return portConditions.allMatches(EMPTY_PORT) ? "empty" : portConditions.describe(); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/main/java/com/github/paweladamski/httpclientmock/UrlParamsParser.java: -------------------------------------------------------------------------------- 1 | package com.github.paweladamski.httpclientmock; 2 | 3 | import java.nio.charset.Charset; 4 | import java.nio.charset.StandardCharsets; 5 | import java.util.Collections; 6 | import java.util.List; 7 | import org.apache.http.NameValuePair; 8 | import org.apache.http.client.utils.URLEncodedUtils; 9 | 10 | public class UrlParamsParser { 11 | 12 | public List parse(String query) { 13 | return parse(query, StandardCharsets.UTF_8); 14 | } 15 | 16 | public List parse(String query, Charset charset) { 17 | if (query != null) { 18 | return URLEncodedUtils.parse(query, charset); 19 | } else { 20 | return Collections.emptyList(); 21 | } 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/github/paweladamski/httpclientmock/UrlParser.java: -------------------------------------------------------------------------------- 1 | package com.github.paweladamski.httpclientmock; 2 | 3 | import static org.hamcrest.Matchers.equalTo; 4 | import static org.hamcrest.Matchers.isEmptyOrNullString; 5 | 6 | import java.net.MalformedURLException; 7 | import java.net.URL; 8 | import java.nio.charset.StandardCharsets; 9 | import java.util.List; 10 | import org.apache.http.NameValuePair; 11 | import org.hamcrest.Matchers; 12 | 13 | public class UrlParser { 14 | 15 | public static final int EMPTY_PORT_NUMBER = -1; 16 | 17 | public UrlConditions parse(String urlText) { 18 | try { 19 | UrlConditions conditions = new UrlConditions(); 20 | URL url = new URL(urlText); 21 | 22 | String ref = url.getRef(); 23 | conditions.setReferenceConditions((ref == null) ? isEmptyOrNullString() : equalTo(ref)); 24 | 25 | conditions.setSchemaConditions(Matchers.equalTo(url.getProtocol())); 26 | conditions.getHostConditions().add(equalTo(url.getHost())); 27 | conditions.getPortConditions().add(equalTo(url.getPort())); 28 | conditions.getPathConditions().add(equalTo(url.getPath())); 29 | List params = new UrlParamsParser().parse(url.getQuery(), StandardCharsets.UTF_8); 30 | for (NameValuePair param : params) { 31 | conditions.getUrlQueryConditions().put(param.getName(), equalTo(param.getValue())); 32 | } 33 | return conditions; 34 | } catch (MalformedURLException e) { 35 | throw new IllegalArgumentException(e); 36 | } 37 | 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/github/paweladamski/httpclientmock/action/Action.java: -------------------------------------------------------------------------------- 1 | package com.github.paweladamski.httpclientmock.action; 2 | 3 | import com.github.paweladamski.httpclientmock.Request; 4 | import java.io.IOException; 5 | import org.apache.http.HttpResponse; 6 | 7 | public interface Action { 8 | 9 | HttpResponse getResponse(Request r) throws IOException; 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/github/paweladamski/httpclientmock/action/CookieAction.java: -------------------------------------------------------------------------------- 1 | package com.github.paweladamski.httpclientmock.action; 2 | 3 | import com.github.paweladamski.httpclientmock.Request; 4 | import java.io.IOException; 5 | import org.apache.http.HttpResponse; 6 | import org.apache.http.client.protocol.HttpClientContext; 7 | import org.apache.http.impl.client.BasicCookieStore; 8 | import org.apache.http.impl.cookie.BasicClientCookie; 9 | 10 | public class CookieAction implements Action { 11 | 12 | private final Action parentAction; 13 | private final String cookieName; 14 | private final String cookieValue; 15 | 16 | public CookieAction(Action parentAction, String cookieName, String cookieValue) { 17 | this.parentAction = parentAction; 18 | this.cookieName = cookieName; 19 | this.cookieValue = cookieValue; 20 | } 21 | 22 | @Override 23 | public HttpResponse getResponse(Request request) throws IOException { 24 | HttpResponse response = parentAction.getResponse(request); 25 | 26 | if (request.getHttpContext() == null) { 27 | throw new RuntimeException("No Http context"); 28 | } 29 | if (!(request.getHttpContext() instanceof HttpClientContext)) { 30 | throw new RuntimeException("Http context is not a HttpClientContext instance."); 31 | } 32 | HttpClientContext httpClientContext = (HttpClientContext) request.getHttpContext(); 33 | if (httpClientContext.getCookieStore() == null) { 34 | httpClientContext.setCookieStore(new BasicCookieStore()); 35 | } 36 | httpClientContext.getCookieStore().addCookie(new BasicClientCookie(cookieName, cookieValue)); 37 | 38 | return response; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/github/paweladamski/httpclientmock/action/ExceptionAction.java: -------------------------------------------------------------------------------- 1 | package com.github.paweladamski.httpclientmock.action; 2 | 3 | import com.github.paweladamski.httpclientmock.Request; 4 | import java.io.IOException; 5 | import org.apache.http.HttpResponse; 6 | 7 | public class ExceptionAction implements Action { 8 | 9 | private final IOException exception; 10 | 11 | public ExceptionAction(IOException e) { 12 | this.exception = e; 13 | } 14 | 15 | @Override 16 | public HttpResponse getResponse(Request request) throws IOException { 17 | throw exception; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/github/paweladamski/httpclientmock/action/HeaderAction.java: -------------------------------------------------------------------------------- 1 | package com.github.paweladamski.httpclientmock.action; 2 | 3 | import com.github.paweladamski.httpclientmock.Request; 4 | import java.io.IOException; 5 | import org.apache.http.HttpResponse; 6 | 7 | public class HeaderAction implements Action { 8 | 9 | private final Action parentAction; 10 | private final String name; 11 | private final String value; 12 | 13 | public HeaderAction(Action parentAction, String name, String value) { 14 | this.parentAction = parentAction; 15 | this.name = name; 16 | this.value = value; 17 | } 18 | 19 | @Override 20 | public HttpResponse getResponse(Request r) throws IOException { 21 | HttpResponse response = parentAction.getResponse(r); 22 | response.addHeader(name, value); 23 | return response; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/github/paweladamski/httpclientmock/action/StatusWithEmptyEntityResponse.java: -------------------------------------------------------------------------------- 1 | package com.github.paweladamski.httpclientmock.action; 2 | 3 | import static org.apache.http.HttpStatus.SC_NO_CONTENT; 4 | 5 | import com.github.paweladamski.httpclientmock.Request; 6 | import java.io.IOException; 7 | import java.util.Optional; 8 | import org.apache.http.HttpResponse; 9 | import org.apache.http.ProtocolVersion; 10 | import org.apache.http.entity.StringEntity; 11 | import org.apache.http.message.BasicHttpResponse; 12 | 13 | public class StatusWithEmptyEntityResponse implements Action { 14 | 15 | private final Optional parentAction; 16 | private final int status; 17 | private final String reason; 18 | 19 | public StatusWithEmptyEntityResponse(int status) { 20 | this.status = status; 21 | this.reason = ""; 22 | this.parentAction = Optional.empty(); 23 | } 24 | 25 | public StatusWithEmptyEntityResponse(Action parentAction, int status) { 26 | this.status = status; 27 | this.reason = ""; 28 | this.parentAction = Optional.of(parentAction); 29 | } 30 | 31 | public StatusWithEmptyEntityResponse(Action parentAction, int status, String reason) { 32 | this.status = status; 33 | this.reason = reason; 34 | this.parentAction = Optional.of(parentAction); 35 | } 36 | 37 | @Override 38 | public HttpResponse getResponse(Request request) throws IOException { 39 | HttpResponse response; 40 | if (parentAction.isPresent()) { 41 | response = parentAction.get().getResponse(request); 42 | } else { 43 | response = new BasicHttpResponse(new ProtocolVersion("http", 1, 1), status, ""); 44 | if (status != SC_NO_CONTENT) { 45 | response.setEntity(new StringEntity("")); 46 | } 47 | } 48 | response.setStatusCode(status); 49 | response.setReasonPhrase(reason); 50 | return response; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/github/paweladamski/httpclientmock/action/StatusWithNullEntityResponse.java: -------------------------------------------------------------------------------- 1 | package com.github.paweladamski.httpclientmock.action; 2 | 3 | import com.github.paweladamski.httpclientmock.Request; 4 | import java.util.Optional; 5 | import org.apache.http.ProtocolVersion; 6 | import org.apache.http.message.BasicHttpResponse; 7 | 8 | public class StatusWithNullEntityResponse implements Action { 9 | 10 | private final Optional reason; 11 | private final int status; 12 | 13 | public StatusWithNullEntityResponse(int status) { 14 | this.status = status; 15 | this.reason = Optional.empty(); 16 | } 17 | 18 | public StatusWithNullEntityResponse(int status, String reason) { 19 | this.status = status; 20 | this.reason = Optional.of(reason); 21 | } 22 | 23 | @Override 24 | public BasicHttpResponse getResponse(Request request) { 25 | return new BasicHttpResponse(new ProtocolVersion("http", 1, 1), status, reason.orElse(null)); 26 | } 27 | } -------------------------------------------------------------------------------- /src/main/java/com/github/paweladamski/httpclientmock/action/StringResponse.java: -------------------------------------------------------------------------------- 1 | package com.github.paweladamski.httpclientmock.action; 2 | 3 | import com.github.paweladamski.httpclientmock.Request; 4 | import java.nio.charset.Charset; 5 | import org.apache.http.HttpResponse; 6 | import org.apache.http.ProtocolVersion; 7 | import org.apache.http.entity.ContentType; 8 | import org.apache.http.entity.StringEntity; 9 | import org.apache.http.message.BasicHttpResponse; 10 | 11 | public class StringResponse implements Action { 12 | 13 | private final int statusCode; 14 | private final String response; 15 | private final Charset charset; 16 | private final ContentType contentType; 17 | 18 | public StringResponse(String response, Charset charset) { 19 | this(200, response, charset); 20 | } 21 | 22 | public StringResponse(int statusCode, String response, Charset charset) { 23 | this(statusCode, response, charset, ContentType.TEXT_PLAIN); 24 | } 25 | 26 | public StringResponse(String response, Charset charset, ContentType contentType) { 27 | this(200, response, charset, contentType); 28 | } 29 | 30 | public StringResponse(int statusCode, String response, Charset charset, ContentType contentType) { 31 | this.statusCode = statusCode; 32 | this.response = response; 33 | this.charset = charset; 34 | this.contentType = contentType; 35 | } 36 | 37 | @Override 38 | public HttpResponse getResponse(Request request) { 39 | BasicHttpResponse response = new BasicHttpResponse(new ProtocolVersion("http", 1, 1), statusCode, "ok"); 40 | StringEntity entity = new StringEntity(this.response, this.charset); 41 | entity.setContentType(contentType.toString()); 42 | response.setEntity(entity); 43 | response.addHeader("Content-type", contentType.toString()); 44 | return response; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/github/paweladamski/httpclientmock/action/UrlEncodedFormEntityResponse.java: -------------------------------------------------------------------------------- 1 | package com.github.paweladamski.httpclientmock.action; 2 | 3 | import java.nio.charset.Charset; 4 | import java.util.Collection; 5 | 6 | import org.apache.http.HttpResponse; 7 | import org.apache.http.NameValuePair; 8 | import org.apache.http.ProtocolVersion; 9 | import org.apache.http.client.entity.UrlEncodedFormEntity; 10 | import org.apache.http.message.BasicHttpResponse; 11 | 12 | import com.github.paweladamski.httpclientmock.Request; 13 | 14 | /** 15 | * @author Michael Angstadt 16 | */ 17 | public class UrlEncodedFormEntityResponse implements Action { 18 | 19 | private final int statusCode; 20 | private final Collection pairs; 21 | private final Charset charset; 22 | 23 | public UrlEncodedFormEntityResponse(Collection pairs, Charset charset) { 24 | this(200, pairs, charset); 25 | } 26 | 27 | public UrlEncodedFormEntityResponse(int statusCode, Collection pairs, Charset charset) { 28 | this.statusCode = statusCode; 29 | this.pairs = pairs; 30 | this.charset = charset; 31 | } 32 | 33 | @Override 34 | public HttpResponse getResponse(Request request) { 35 | BasicHttpResponse response = new BasicHttpResponse(new ProtocolVersion("http", 1, 1), statusCode, "ok"); 36 | 37 | UrlEncodedFormEntity entity = new UrlEncodedFormEntity(pairs, charset); 38 | response.setEntity(entity); 39 | 40 | return response; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/github/paweladamski/httpclientmock/condition/BodyMatcher.java: -------------------------------------------------------------------------------- 1 | package com.github.paweladamski.httpclientmock.condition; 2 | 3 | import com.github.paweladamski.httpclientmock.Debugger; 4 | import com.github.paweladamski.httpclientmock.Request; 5 | import java.io.IOException; 6 | import org.apache.http.HttpEntity; 7 | import org.apache.http.HttpEntityEnclosingRequest; 8 | import org.apache.http.HttpRequest; 9 | import org.apache.http.util.EntityUtils; 10 | import org.hamcrest.Matcher; 11 | 12 | public class BodyMatcher implements Condition { 13 | 14 | private final Matcher matcher; 15 | 16 | public BodyMatcher(Matcher matcher) { 17 | this.matcher = matcher; 18 | } 19 | 20 | @Override 21 | public boolean matches(Request request) { 22 | HttpRequest httpRequest = request.getHttpRequest(); 23 | if (!(httpRequest instanceof HttpEntityEnclosingRequest)) { 24 | return false; 25 | } 26 | 27 | HttpEntity entity = ((HttpEntityEnclosingRequest) httpRequest).getEntity(); 28 | if (entity == null) { 29 | return false; 30 | } 31 | 32 | String message; 33 | try { 34 | message = EntityUtils.toString(entity); 35 | } catch (IOException e) { 36 | return false; 37 | } 38 | return matcher.matches(message); 39 | } 40 | 41 | @Override 42 | public void debug(Request request, Debugger debugger) { 43 | debugger.message(matches(request), "body matches"); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/github/paweladamski/httpclientmock/condition/Condition.java: -------------------------------------------------------------------------------- 1 | package com.github.paweladamski.httpclientmock.condition; 2 | 3 | import com.github.paweladamski.httpclientmock.Debugger; 4 | import com.github.paweladamski.httpclientmock.Request; 5 | import org.apache.http.HttpHost; 6 | import org.apache.http.HttpRequest; 7 | import org.apache.http.protocol.HttpContext; 8 | 9 | public interface Condition { 10 | 11 | default boolean matches(HttpHost httpHost, HttpRequest httpRequest, HttpContext httpContext) { 12 | return matches(new Request(httpHost, httpRequest, httpContext)); 13 | } 14 | 15 | boolean matches(Request request); 16 | 17 | default void debug(Request request, Debugger debugger) { 18 | debugger.message(matches(request), getClass().getSimpleName()); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/github/paweladamski/httpclientmock/condition/HeaderCondition.java: -------------------------------------------------------------------------------- 1 | package com.github.paweladamski.httpclientmock.condition; 2 | 3 | import com.github.paweladamski.httpclientmock.Debugger; 4 | import com.github.paweladamski.httpclientmock.Request; 5 | import org.hamcrest.Matcher; 6 | import org.hamcrest.StringDescription; 7 | 8 | public class HeaderCondition implements Condition { 9 | 10 | private final String header; 11 | private final Matcher value; 12 | 13 | public HeaderCondition(String header, Matcher value) { 14 | this.header = header; 15 | this.value = value; 16 | } 17 | 18 | @Override 19 | public boolean matches(Request request) { 20 | return request.getHttpRequest().getFirstHeader(header) != null && 21 | value.matches(request.getHttpRequest().getFirstHeader(header).getValue()); 22 | } 23 | 24 | @Override 25 | public void debug(Request request, Debugger debugger) { 26 | String matcherDesc = StringDescription.toString(value); 27 | debugger.message(matches(request), "header " + header + " is " + matcherDesc); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/github/paweladamski/httpclientmock/condition/HttpMethodCondition.java: -------------------------------------------------------------------------------- 1 | package com.github.paweladamski.httpclientmock.condition; 2 | 3 | import com.github.paweladamski.httpclientmock.Debugger; 4 | import com.github.paweladamski.httpclientmock.Request; 5 | 6 | public class HttpMethodCondition implements Condition { 7 | 8 | private final String method; 9 | 10 | public HttpMethodCondition(String method) { 11 | this.method = method; 12 | } 13 | 14 | @Override 15 | public boolean matches(Request request) { 16 | return request.getHttpRequest().getRequestLine().getMethod().equals(method); 17 | } 18 | 19 | @Override 20 | public void debug(Request request, Debugger debugger) { 21 | debugger.message(matches(request), "HTTP method is " + method); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/github/paweladamski/httpclientmock/condition/UrlEncodedFormCondition.java: -------------------------------------------------------------------------------- 1 | package com.github.paweladamski.httpclientmock.condition; 2 | 3 | import com.github.paweladamski.httpclientmock.Debugger; 4 | import com.github.paweladamski.httpclientmock.Request; 5 | import com.github.paweladamski.httpclientmock.matchers.ParametersMatcher; 6 | import java.util.List; 7 | import java.util.Set; 8 | import org.apache.http.NameValuePair; 9 | import org.hamcrest.Matcher; 10 | 11 | /** 12 | * Tests the request body for URL-encoded parameters. 13 | * 14 | * @author Michael Angstadt 15 | */ 16 | public class UrlEncodedFormCondition implements Condition { 17 | 18 | private ParametersMatcher expectedParameters = new ParametersMatcher(); 19 | private boolean allowExtraParameters = false; 20 | 21 | @Override 22 | public boolean matches(Request r) { 23 | List actualParameters = new UrlEncodedFormParser().parse(r); 24 | if (allowExtraParameters) { 25 | return expectedParameters.matchesAndAllowExtraParameters(actualParameters); 26 | } else { 27 | return expectedParameters.matches(actualParameters); 28 | } 29 | } 30 | 31 | /** 32 | * Adds an expected form parameter. 33 | * 34 | * @param name the parameter name 35 | * @param matcher the expected value 36 | */ 37 | public void addExpectedParameter(String name, Matcher matcher) { 38 | expectedParameters.put(name, matcher); 39 | } 40 | 41 | /** 42 | * Adds expected form parameters. 43 | * 44 | * @param parameters the expected parameters 45 | */ 46 | public void addExpectedParameters(ParametersMatcher parameters) { 47 | expectedParameters.putAll(parameters); 48 | } 49 | 50 | @Override 51 | public void debug(Request r, Debugger debugger) { 52 | List actual = new UrlEncodedFormParser().parse(r); 53 | 54 | Set missingParams = expectedParameters.findMissingParameters(actual); 55 | for (String param : missingParams) { 56 | debugger.message(false, "form parameter " + param + " is missing from the request"); 57 | } 58 | 59 | for (NameValuePair param : actual) { 60 | if (expectedParameters.containsParameter(param.getName())) { 61 | boolean matches = expectedParameters.matches(param.getName(), param.getValue()); 62 | String message = "form parameter " + param.getName() + " is " + expectedParameters.get(param.getName()).describe(); 63 | debugger.message(matches, message); 64 | } else if (!allowExtraParameters) { 65 | String message = "form parameter " + param.getName() + " was not expected to be in the request"; 66 | debugger.message(false, message); 67 | } 68 | } 69 | } 70 | 71 | public void setAllowExtraParameters(boolean allowExtraParameters) { 72 | this.allowExtraParameters = allowExtraParameters; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/com/github/paweladamski/httpclientmock/condition/UrlEncodedFormParser.java: -------------------------------------------------------------------------------- 1 | package com.github.paweladamski.httpclientmock.condition; 2 | 3 | import com.github.paweladamski.httpclientmock.Request; 4 | import java.io.IOException; 5 | import java.util.Collections; 6 | import java.util.List; 7 | import org.apache.http.HttpEntity; 8 | import org.apache.http.HttpEntityEnclosingRequest; 9 | import org.apache.http.NameValuePair; 10 | import org.apache.http.client.utils.URLEncodedUtils; 11 | 12 | public class UrlEncodedFormParser { 13 | 14 | List parse(Request request) { 15 | if (!requestHasBody(request)) { 16 | return Collections.emptyList(); 17 | } 18 | 19 | HttpEntityEnclosingRequest httpRequest = (HttpEntityEnclosingRequest) request.getHttpRequest(); 20 | HttpEntity entity = httpRequest.getEntity(); 21 | if (entity == null) { 22 | return Collections.emptyList(); 23 | } 24 | 25 | try { 26 | /* 27 | * The method below returns an empty list if the Content-Type of the 28 | * request is not "application/x-www-form-urlencoded". So, requests with 29 | * other kinds of data in the body will correctly be ignored here. 30 | */ 31 | return URLEncodedUtils.parse(entity); 32 | } catch (IOException e) { 33 | throw new RuntimeException(e); 34 | } 35 | } 36 | 37 | private boolean requestHasBody(Request r) { 38 | return (r.getHttpRequest() instanceof HttpEntityEnclosingRequest); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/github/paweladamski/httpclientmock/matchers/HttpResponseMatchers.java: -------------------------------------------------------------------------------- 1 | package com.github.paweladamski.httpclientmock.matchers; 2 | 3 | import java.io.IOException; 4 | import java.util.Optional; 5 | import org.apache.http.HttpResponse; 6 | import org.apache.http.client.CookieStore; 7 | import org.apache.http.client.protocol.HttpClientContext; 8 | import org.apache.http.cookie.Cookie; 9 | import org.apache.http.util.EntityUtils; 10 | import org.hamcrest.BaseMatcher; 11 | import org.hamcrest.Description; 12 | import org.hamcrest.Matcher; 13 | 14 | public final class HttpResponseMatchers { 15 | 16 | public static Matcher hasStatus(int expectedStatus) { 17 | return new BaseMatcher() { 18 | public boolean matches(Object o) { 19 | HttpResponse response = (HttpResponse) o; 20 | return response.getStatusLine().getStatusCode() == expectedStatus; 21 | } 22 | 23 | public void describeTo(Description description) { 24 | description.appendValue(expectedStatus); 25 | } 26 | }; 27 | } 28 | 29 | public static Matcher hasNoEntity() { 30 | return new BaseMatcher() { 31 | public boolean matches(Object o) { 32 | HttpResponse response = (HttpResponse) o; 33 | return response.getEntity() == null; 34 | } 35 | 36 | public void describeTo(Description description) { 37 | description.appendValue(null); 38 | } 39 | }; 40 | } 41 | 42 | public static Matcher hasReason(String expectedReason) { 43 | return new BaseMatcher() { 44 | public boolean matches(Object o) { 45 | HttpResponse response = (HttpResponse) o; 46 | return response.getStatusLine().getReasonPhrase().equals(expectedReason); 47 | } 48 | 49 | public void describeTo(Description description) { 50 | description.appendValue(expectedReason); 51 | } 52 | }; 53 | } 54 | 55 | public static Matcher hasContent(final String content) { 56 | return hasContent(content, "UTF-8"); 57 | } 58 | 59 | public static Matcher hasContent(final String content, final String charset) { 60 | return new BaseMatcher() { 61 | public boolean matches(Object o) { 62 | HttpResponse response = (HttpResponse) o; 63 | 64 | String targetString; 65 | try { 66 | byte[] bytes = EntityUtils.toByteArray(response.getEntity()); 67 | targetString = new String(bytes, charset); 68 | } catch (IOException e) { 69 | e.printStackTrace(); 70 | return false; 71 | } 72 | 73 | return targetString.equals(content); 74 | } 75 | 76 | public void describeTo(Description description) { 77 | description.appendText(content); 78 | } 79 | }; 80 | } 81 | 82 | public static Matcher hasCookie(final String expectedCookieName, final String expectedCookieValue) { 83 | return new BaseMatcher() { 84 | public boolean matches(Object o) { 85 | HttpClientContext httpClientContext = (HttpClientContext) o; 86 | String cookieValue = getCookieValue(httpClientContext.getCookieStore(), expectedCookieName); 87 | return expectedCookieValue.equals(cookieValue); 88 | } 89 | 90 | public void describeTo(Description description) { 91 | description.appendValue(expectedCookieValue); 92 | } 93 | 94 | private String getCookieValue(CookieStore cookieStore, String cookieName) { 95 | if (cookieStore == null) { 96 | return null; 97 | } 98 | 99 | return cookieStore.getCookies().stream() 100 | .filter(c -> c.getName().equalsIgnoreCase(cookieName)) 101 | .findFirst().map(c -> c.getValue()).orElse(null); 102 | } 103 | }; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/main/java/com/github/paweladamski/httpclientmock/matchers/MatchersList.java: -------------------------------------------------------------------------------- 1 | package com.github.paweladamski.httpclientmock.matchers; 2 | 3 | import java.util.ArrayList; 4 | import org.hamcrest.Matcher; 5 | import org.hamcrest.StringDescription; 6 | 7 | public class MatchersList extends ArrayList> { 8 | 9 | public boolean allMatches(T value) { 10 | return this.stream() 11 | .allMatch(m -> m.matches(value)); 12 | } 13 | 14 | public String describe() { 15 | return this.stream().map(StringDescription::toString).reduce((a, b) -> a + " and " + b).orElse(""); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/github/paweladamski/httpclientmock/matchers/ParametersMatcher.java: -------------------------------------------------------------------------------- 1 | package com.github.paweladamski.httpclientmock.matchers; 2 | 3 | import java.util.HashMap; 4 | import java.util.List; 5 | import java.util.Set; 6 | import java.util.function.Predicate; 7 | import java.util.stream.Collectors; 8 | import org.apache.http.NameValuePair; 9 | import org.hamcrest.Matcher; 10 | 11 | public class ParametersMatcher { 12 | 13 | private HashMap> matchers = new HashMap<>(); 14 | 15 | public void put(String name, Matcher value) { 16 | matchers.computeIfAbsent(name, n -> new MatchersList<>()).add(value); 17 | } 18 | 19 | public void putAll(String name, MatchersList value) { 20 | matchers.computeIfAbsent(name, n -> new MatchersList<>()).addAll(value); 21 | } 22 | 23 | public MatchersList get(String name) { 24 | return matchers.get(name); 25 | } 26 | 27 | public boolean containsParameter(String name) { 28 | return matchers.containsKey(name); 29 | } 30 | 31 | public void putAll(ParametersMatcher parametersMatcher) { 32 | for (String paramName : parametersMatcher.matchers.keySet()) { 33 | putAll(paramName, parametersMatcher.matchers.get(paramName)); 34 | } 35 | } 36 | 37 | public boolean matchesAndAllowExtraParameters(List actual) { 38 | return findMissingParameters(actual).isEmpty() 39 | && allParametersHaveMatchingValue(actual); 40 | } 41 | 42 | public boolean matches(List actual) { 43 | return findRedundantParams(actual).isEmpty() 44 | && findMissingParameters(actual).isEmpty() 45 | && allParametersHaveMatchingValue(actual); 46 | } 47 | 48 | public boolean matches(String name, String value) { 49 | return matchers.containsKey(name) && matchers.get(name).allMatches(value); 50 | } 51 | 52 | private boolean allParametersHaveMatchingValue(List actual) { 53 | return actual.stream().allMatch(param -> matchers.getOrDefault(param.getName(), new MatchersList<>()).allMatches(param.getValue())); 54 | } 55 | 56 | public Set findRedundantParams(List actualParameters) { 57 | return actualParameters.stream() 58 | .map(NameValuePair::getName) 59 | .filter(n -> !matchers.containsKey(n)) 60 | .collect(Collectors.toSet()); 61 | } 62 | 63 | public Set findMissingParameters(List actualParameters) { 64 | List actualParametersName = actualParameters.stream().map(NameValuePair::getName).collect(Collectors.toList()); 65 | return matchers.keySet().stream() 66 | .filter(((Predicate) actualParametersName::contains).negate()) 67 | .collect(Collectors.toSet()); 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/com/github/paweladamski/httpclientmock/matchers/UrlQueryMatcher.java: -------------------------------------------------------------------------------- 1 | package com.github.paweladamski.httpclientmock.matchers; 2 | 3 | import com.github.paweladamski.httpclientmock.Debugger; 4 | import com.github.paweladamski.httpclientmock.UrlParamsParser; 5 | import java.util.List; 6 | import java.util.Set; 7 | import org.apache.http.NameValuePair; 8 | import org.hamcrest.Matcher; 9 | 10 | public class UrlQueryMatcher { 11 | 12 | private ParametersMatcher expected = new ParametersMatcher(); 13 | private boolean allowExtraParameters; 14 | 15 | public boolean matches(String query) { 16 | List actualParameters = new UrlParamsParser().parse(query); 17 | if (allowExtraParameters) { 18 | return expected.matchesAndAllowExtraParameters(actualParameters); 19 | } else { 20 | return expected.matches(actualParameters); 21 | } 22 | } 23 | 24 | public void put(String name, Matcher matcher) { 25 | expected.put(name, matcher); 26 | } 27 | 28 | public void describe(String query, Debugger debugger) { 29 | List actualParameters = new UrlParamsParser().parse(query); 30 | Set missingParams = expected.findMissingParameters(actualParameters); 31 | for (String param : missingParams) { 32 | debugger.message(false, "query parameter " + param + " is missing from the request"); 33 | } 34 | 35 | for (NameValuePair param : actualParameters) { 36 | if (expected.containsParameter(param.getName())) { 37 | boolean matches = expected.matches(param.getName(), param.getValue()); 38 | String message = "query parameter " + param.getName() + " is " + expected.get(param.getName()).describe(); 39 | debugger.message(matches, message); 40 | } else if (!allowExtraParameters){ 41 | String message = "query parameter " + param.getName() + " was not expected to be in the request"; 42 | debugger.message(false, message); 43 | } 44 | } 45 | } 46 | 47 | public void setAllowExtraParameters(boolean allowExtraParameters) { 48 | this.allowExtraParameters = allowExtraParameters; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/test/java/com/github/paweladamski/httpclientmock/Asserts.java: -------------------------------------------------------------------------------- 1 | package com.github.paweladamski.httpclientmock; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | 5 | @SuppressWarnings("unchecked") 6 | class Asserts { 7 | 8 | static T assertThrows(Class expected, ThrowingRunnable action) throws Exception { 9 | try { 10 | action.run(); 11 | Assertions.fail("Did not throw expected " + expected.getSimpleName()); 12 | return null; // never actually 13 | } catch (Exception actual) { 14 | if (!expected.isAssignableFrom(actual.getClass())) { // runtime '!(actual instanceof expected)' 15 | System.err.println("Threw " + actual.getClass().getSimpleName() 16 | + ", which is not a subtype of expected " 17 | + expected.getSimpleName()); 18 | throw actual; // throw the unexpected Throwable for maximum transparency 19 | } else { 20 | return (T) actual; // return the expected Throwable for further examination 21 | } 22 | } 23 | } 24 | } 25 | 26 | -------------------------------------------------------------------------------- /src/test/java/com/github/paweladamski/httpclientmock/DebuggingTest.java: -------------------------------------------------------------------------------- 1 | package com.github.paweladamski.httpclientmock; 2 | 3 | import static com.github.paweladamski.httpclientmock.Requests.httpGet; 4 | import static com.github.paweladamski.httpclientmock.Requests.httpPost; 5 | import static org.hamcrest.MatcherAssert.assertThat; 6 | import static org.hamcrest.Matchers.hasItem; 7 | import static org.hamcrest.Matchers.not; 8 | import static org.junit.jupiter.api.Assertions.assertFalse; 9 | import static org.junit.jupiter.api.Assertions.assertTrue; 10 | 11 | import java.io.IOException; 12 | import java.util.ArrayList; 13 | import java.util.Arrays; 14 | import java.util.List; 15 | import org.apache.http.client.entity.UrlEncodedFormEntity; 16 | import org.apache.http.client.methods.HttpGet; 17 | import org.apache.http.client.methods.HttpPost; 18 | import org.apache.http.message.BasicNameValuePair; 19 | import org.hamcrest.Matchers; 20 | import org.junit.jupiter.api.BeforeEach; 21 | import org.junit.jupiter.api.Test; 22 | 23 | public class DebuggingTest { 24 | 25 | private HttpClientMock httpClientMock; 26 | private TestDebugger debugger; 27 | 28 | @BeforeEach 29 | public void setUp() { 30 | debugger = new TestDebugger(); 31 | httpClientMock = new HttpClientMock("http://localhost", debugger); 32 | } 33 | 34 | @Test 35 | public void should_print_all_request_with_no_matching_rules() throws IOException { 36 | httpClientMock.onGet("/admin").doReturn("admin"); 37 | 38 | httpClientMock.execute(new HttpGet("http://localhost/login")); 39 | httpClientMock.execute(new HttpGet("http://localhost/admin")); 40 | 41 | assertThat(debugger.requests, hasItem("http://localhost/login")); 42 | assertThat(debugger.requests, not(hasItem("http://localhost/admin"))); 43 | } 44 | 45 | @Test 46 | public void should_print_all_request_when_debugging_is_turn_on() throws IOException { 47 | httpClientMock.onGet("/login").doReturn("login"); 48 | httpClientMock.onGet("/user").doReturn("user"); 49 | httpClientMock.onGet("/admin").doReturn("admin"); 50 | 51 | httpClientMock.debugOn(); 52 | httpClientMock.execute(new HttpGet("http://localhost/login")); 53 | httpClientMock.execute(new HttpGet("http://localhost/user")); 54 | httpClientMock.debugOff(); 55 | httpClientMock.execute(new HttpGet("http://localhost/admin")); 56 | 57 | assertThat(debugger.requests, hasItem("http://localhost/login")); 58 | assertThat(debugger.requests, hasItem("http://localhost/user")); 59 | assertThat(debugger.requests, not(hasItem("http://localhost/admin"))); 60 | } 61 | 62 | @Test 63 | public void should_debug_header_condition() throws IOException { 64 | httpClientMock 65 | .onGet("/login").withHeader("User-Agent", "Mozilla") 66 | .doReturn("mozilla"); 67 | 68 | HttpGet getMozilla = new HttpGet("http://localhost/login"); 69 | HttpGet getChrome = new HttpGet("http://localhost/login"); 70 | getMozilla.addHeader("User-Agent", "Mozilla"); 71 | getChrome.addHeader("User-Agent", "Chrome"); 72 | 73 | httpClientMock.debugOn(); 74 | httpClientMock.execute(getMozilla); 75 | httpClientMock.execute(getChrome); 76 | httpClientMock.debugOff(); 77 | 78 | assertTrue(debugger.matching.contains("header User-Agent is \"Mozilla\"")); 79 | assertFalse(debugger.notMatching.contains("header User-Agent is \"Chrome\"")); 80 | } 81 | 82 | @Test 83 | public void should_put_message_about_missing_parameter() throws IOException { 84 | httpClientMock.onGet("/login?foo=bar"); 85 | httpClientMock.execute(httpGet("http://localhost/login")); 86 | assertTrue(debugger.notMatching.contains("query parameter foo is missing from the request")); 87 | } 88 | 89 | @Test 90 | public void should_put_message_about_matching_parameter() throws IOException { 91 | httpClientMock 92 | .onGet("/login").withParameter("foo", "bar") 93 | .doReturn("login"); 94 | httpClientMock.debugOn(); 95 | httpClientMock.execute(httpGet("http://localhost/login?foo=bar")); 96 | assertTrue(debugger.matching.contains("query parameter foo is \"bar\"")); 97 | } 98 | 99 | @Test 100 | public void should_put_message_about_not_matching_parameter() throws IOException { 101 | httpClientMock.onGet("/login") 102 | .withParameter("foo", "bar") 103 | .doReturn("login"); 104 | httpClientMock.execute(httpGet("http://localhost/login?foo=bbb")); 105 | assertTrue(debugger.notMatching.contains("query parameter foo is \"bar\"")); 106 | } 107 | 108 | @Test 109 | public void should_put_message_about_redundant_parameter() throws IOException { 110 | httpClientMock.onGet("/login") 111 | .withoutExtraParameters() 112 | .doReturn("login"); 113 | httpClientMock.execute(httpGet("http://localhost/login?foo=bbb")); 114 | assertTrue(debugger.notMatching.contains("query parameter foo was not expected to be in the request")); 115 | } 116 | 117 | @Test 118 | public void should_put_message_with_all_parameter_matchers() throws IOException { 119 | httpClientMock.onGet("/login") 120 | .withParameter("foo", Matchers.startsWith("a")) 121 | .withParameter("foo", Matchers.endsWith("b")) 122 | .doReturn("login"); 123 | httpClientMock.debugOn(); 124 | httpClientMock.execute(httpGet("http://localhost/login?foo=aabb")); 125 | assertTrue(debugger.matching.contains("query parameter foo is a string starting with \"a\" and a string ending with \"b\"")); 126 | } 127 | 128 | @Test 129 | public void should_put_message_about_not_matching_reference() throws IOException { 130 | httpClientMock.onGet("/login#foo") 131 | .doReturn("login"); 132 | httpClientMock.execute(httpGet("http://localhost/login")); 133 | assertTrue(debugger.notMatching.contains("reference is \"foo\"")); 134 | } 135 | 136 | @Test 137 | public void should_put_message_about_matching_reference() throws IOException { 138 | httpClientMock.onGet("/login#foo") 139 | .doReturn("login"); 140 | httpClientMock.debugOn(); 141 | httpClientMock.execute(httpGet("http://localhost/login#foo")); 142 | assertTrue(debugger.matching.contains("reference is \"foo\"")); 143 | } 144 | 145 | @Test 146 | public void should_not_put_message_about_reference_when_it_is_not_used() throws IOException { 147 | httpClientMock.onGet("/login").doReturn("login"); 148 | httpClientMock.debugOn(); 149 | httpClientMock.execute(httpGet("http://localhost/login")); 150 | assertTrue(debugger.matching.stream().noneMatch(s -> s.startsWith("reference"))); 151 | assertTrue(debugger.notMatching.stream().noneMatch(s -> s.startsWith("reference"))); 152 | } 153 | 154 | @Test 155 | public void should_put_message_about_matching_http_method() throws IOException { 156 | httpClientMock.onGet("/login").doReturn("login"); 157 | httpClientMock.debugOn(); 158 | httpClientMock.execute(httpGet("http://localhost/login")); 159 | assertTrue(debugger.matching.contains("HTTP method is GET")); 160 | } 161 | 162 | @Test 163 | public void should_put_message_about_not_matching_http_method() throws IOException { 164 | httpClientMock.onGet("/login").doReturn("login"); 165 | httpClientMock.debugOn(); 166 | httpClientMock.execute(httpPost("http://localhost/login")); 167 | assertTrue(debugger.notMatching.contains("HTTP method is GET")); 168 | } 169 | 170 | @Test 171 | public void should_put_message_about_not_matching_URL() throws IOException { 172 | httpClientMock.onGet("http://localhost:8080/login").doReturn("login"); 173 | httpClientMock.debugOn(); 174 | httpClientMock.execute(httpPost("https://www.google.com")); 175 | assertTrue(debugger.notMatching.contains("schema is \"http\"")); 176 | assertTrue(debugger.notMatching.contains("host is \"localhost\"")); 177 | assertTrue(debugger.notMatching.contains("path is \"/login\"")); 178 | assertTrue(debugger.notMatching.contains("port is <8080>")); 179 | } 180 | 181 | @Test 182 | public void should_put_message_about_matching_URL() throws IOException { 183 | httpClientMock.onGet("http://localhost:8080/login").doReturn("login"); 184 | httpClientMock.debugOn(); 185 | httpClientMock.execute(httpPost("http://localhost:8080/login")); 186 | assertTrue(debugger.matching.contains("schema is \"http\"")); 187 | assertTrue(debugger.matching.contains("host is \"localhost\"")); 188 | assertTrue(debugger.matching.contains("path is \"/login\"")); 189 | assertTrue(debugger.matching.contains("port is <8080>")); 190 | } 191 | 192 | @Test 193 | public void should_not_put_message_not_expected_query_parameters_when_ExtraParametersAreAllowed() throws IOException { 194 | httpClientMock.onGet("http://localhost:8080/login") 195 | .withExtraParameters() 196 | .doReturn("login"); 197 | httpClientMock.debugOn(); 198 | httpClientMock.execute(httpPost("http://localhost:8080/login?foo=bar")); 199 | assertFalse(debugger.notMatching.contains("query parameter foo was not expected to be in the request")); 200 | } 201 | 202 | @Test 203 | public void should_put_message_not_expected_form_parameters_when_ExtraParametersAreDisAllowed() throws IOException { 204 | httpClientMock.onPost("http://localhost:8080/login") 205 | .withoutExtraFormParameters() 206 | .doReturn("login"); 207 | httpClientMock.debugOn(); 208 | 209 | HttpPost request = new HttpPost("http://localhost/login"); 210 | request.setEntity(new UrlEncodedFormEntity(Arrays.asList( 211 | new BasicNameValuePair("foo", "bar") 212 | ))); 213 | httpClientMock.execute(request); 214 | assertTrue(debugger.notMatching.contains("form parameter foo was not expected to be in the request")); 215 | } 216 | 217 | @Test 218 | public void should_not_put_message_not_expected_form_parameters_when_ExtraParametersAreAllowed() throws IOException { 219 | httpClientMock.onPost("http://localhost:8080/login") 220 | .withExtraFormParameters() 221 | .doReturn("login"); 222 | httpClientMock.debugOn(); 223 | 224 | HttpPost request = new HttpPost("http://localhost/login"); 225 | request.setEntity(new UrlEncodedFormEntity(Arrays.asList( 226 | new BasicNameValuePair("foo", "bar") 227 | ))); 228 | httpClientMock.execute(request); 229 | assertFalse(debugger.notMatching.contains("form parameter foo was not expected to be in the request")); 230 | } 231 | 232 | } 233 | 234 | class TestDebugger extends Debugger { 235 | 236 | public final ArrayList matching = new ArrayList<>(); 237 | public final ArrayList notMatching = new ArrayList<>(); 238 | public final ArrayList requests = new ArrayList<>(); 239 | 240 | @Override 241 | public Rule debug(List rules, Request request) { 242 | this.requests.add(request.getUri()); 243 | return super.debug(rules, request); 244 | } 245 | 246 | @Override 247 | public void message(boolean matching, String expected) { 248 | super.message(matching, expected); 249 | if (matching) { 250 | this.matching.add(expected); 251 | } else { 252 | this.notMatching.add(expected); 253 | } 254 | } 255 | } 256 | -------------------------------------------------------------------------------- /src/test/java/com/github/paweladamski/httpclientmock/HttpClientMockBuilderTest.java: -------------------------------------------------------------------------------- 1 | package com.github.paweladamski.httpclientmock; 2 | 3 | import static com.github.paweladamski.httpclientmock.Requests.httpPost; 4 | import static com.github.paweladamski.httpclientmock.matchers.HttpResponseMatchers.hasContent; 5 | import static com.github.paweladamski.httpclientmock.matchers.HttpResponseMatchers.hasStatus; 6 | import static org.hamcrest.MatcherAssert.assertThat; 7 | import static org.hamcrest.Matchers.containsString; 8 | 9 | import com.github.paweladamski.httpclientmock.condition.Condition; 10 | import com.github.paweladamski.httpclientmock.matchers.ParametersMatcher; 11 | import java.io.IOException; 12 | import java.util.Arrays; 13 | import java.util.Collections; 14 | import org.apache.http.HttpHost; 15 | import org.apache.http.HttpResponse; 16 | import org.apache.http.client.entity.UrlEncodedFormEntity; 17 | import org.apache.http.client.methods.HttpDelete; 18 | import org.apache.http.client.methods.HttpGet; 19 | import org.apache.http.client.methods.HttpHead; 20 | import org.apache.http.client.methods.HttpOptions; 21 | import org.apache.http.client.methods.HttpPatch; 22 | import org.apache.http.client.methods.HttpPost; 23 | import org.apache.http.client.methods.HttpPut; 24 | import org.apache.http.message.BasicNameValuePair; 25 | import org.hamcrest.Matchers; 26 | import org.junit.jupiter.api.Test; 27 | 28 | public class HttpClientMockBuilderTest { 29 | 30 | @Test 31 | public void shouldMatchSeparateHostAndPath() throws IOException { 32 | HttpClientMock httpClientMock = new HttpClientMock(); 33 | 34 | httpClientMock.onPost() 35 | .withHost("http://localhost") 36 | .withPath("/login") 37 | .doReturnStatus(200); 38 | 39 | HttpResponse ok = httpClientMock.execute(new HttpPost("http://localhost/login")); 40 | assertThat(ok, hasStatus(200)); 41 | } 42 | 43 | @Test 44 | public void shouldMatchSeparatePathAndParameter() throws IOException { 45 | HttpClientMock httpClientMock = new HttpClientMock("http://localhost"); 46 | 47 | httpClientMock.onPost() 48 | .withPath("/login") 49 | .withParameter("a", "1") 50 | .doReturn("one"); 51 | httpClientMock.onPost() 52 | .withPath("/login") 53 | .withParameter("b", "2") 54 | .withExtraParameters() 55 | .doReturn("two"); 56 | 57 | HttpResponse one = httpClientMock.execute(new HttpPost("http://localhost/login?a=1")); 58 | HttpResponse two = httpClientMock.execute(new HttpPost("http://localhost/login?b=2")); 59 | HttpResponse three = httpClientMock.execute(new HttpPost("http://localhost/login?a=1&b=2")); 60 | assertThat(one, hasContent("one")); 61 | assertThat(two, hasContent("two")); 62 | assertThat(three, hasContent("two")); 63 | } 64 | 65 | @Test 66 | public void shouldUseRightMethod() throws IOException { 67 | HttpClientMock httpClientMock = new HttpClientMock("http://localhost"); 68 | 69 | httpClientMock.onGet("/foo").doReturn("get"); 70 | httpClientMock.onPost("/foo").doReturn("post"); 71 | httpClientMock.onPut("/foo").doReturn("put"); 72 | httpClientMock.onDelete("/foo").doReturn("delete"); 73 | httpClientMock.onHead("/foo").doReturn("head"); 74 | httpClientMock.onOptions("/foo").doReturn("options"); 75 | httpClientMock.onPatch("/foo").doReturn("patch"); 76 | 77 | HttpResponse getResponse = httpClientMock.execute(new HttpGet("http://localhost/foo")); 78 | HttpResponse postResponse = httpClientMock.execute(new HttpPost("http://localhost/foo")); 79 | HttpResponse putResponse = httpClientMock.execute(new HttpPut("http://localhost/foo")); 80 | HttpResponse deleteResponse = httpClientMock.execute(new HttpDelete("http://localhost/foo")); 81 | HttpResponse headResponse = httpClientMock.execute(new HttpHead("http://localhost/foo")); 82 | HttpResponse optionsResponse = httpClientMock.execute(new HttpOptions("http://localhost/foo")); 83 | HttpResponse patchResponse = httpClientMock.execute(new HttpPatch("http://localhost/foo")); 84 | 85 | assertThat(getResponse, hasContent("get")); 86 | assertThat(postResponse, hasContent("post")); 87 | assertThat(putResponse, hasContent("put")); 88 | assertThat(deleteResponse, hasContent("delete")); 89 | assertThat(headResponse, hasContent("head")); 90 | assertThat(optionsResponse, hasContent("options")); 91 | assertThat(patchResponse, hasContent("patch")); 92 | } 93 | 94 | @Test 95 | public void should_check_custom_rule() throws IOException { 96 | HttpClientMock httpClientMock = new HttpClientMock("http://localhost"); 97 | 98 | Condition fooCondition = (request) -> request.getUri().contains("foo"); 99 | 100 | httpClientMock.onGet("http://localhost/foo/bar") 101 | .with(fooCondition) 102 | .doReturn("yes"); 103 | 104 | HttpResponse first = httpClientMock.execute(new HttpGet("http://localhost/foo/bar")); 105 | 106 | assertThat(first, hasContent("yes")); 107 | } 108 | 109 | @Test 110 | public void should_use_right_host_and_path() throws IOException { 111 | HttpClientMock httpClientMock = new HttpClientMock(); 112 | 113 | httpClientMock.onGet("http://localhost:8080/foo").doReturn("localhost"); 114 | httpClientMock.onGet("http://www.google.com").doReturn("google"); 115 | httpClientMock.onGet("https://www.google.com").doReturn("https"); 116 | 117 | HttpResponse localhost = httpClientMock.execute(new HttpGet("http://localhost:8080/foo")); 118 | HttpResponse google = httpClientMock.execute(new HttpGet("http://www.google.com")); 119 | HttpResponse https = httpClientMock.execute(new HttpGet("https://www.google.com")); 120 | 121 | assertThat(localhost, hasContent("localhost")); 122 | assertThat(google, hasContent("google")); 123 | assertThat(https, hasContent("https")); 124 | } 125 | 126 | @Test 127 | public void should_match_right_header_value() throws IOException { 128 | HttpClientMock httpClientMock = new HttpClientMock("http://localhost:8080"); 129 | 130 | httpClientMock 131 | .onGet("/login").withHeader("User-Agent", "Mozilla") 132 | .doReturn("mozilla"); 133 | httpClientMock 134 | .onGet("/login").withHeader("User-Agent", "Chrome") 135 | .doReturn("chrome"); 136 | 137 | HttpGet getMozilla = new HttpGet("http://localhost:8080/login"); 138 | HttpGet getChrome = new HttpGet("http://localhost:8080/login"); 139 | HttpGet getSafari = new HttpGet("http://localhost:8080/login"); 140 | getMozilla.addHeader("User-Agent", "Mozilla"); 141 | getChrome.addHeader("User-Agent", "Chrome"); 142 | getSafari.addHeader("User-Agent", "Safari"); 143 | 144 | assertThat(httpClientMock.execute(getMozilla), hasContent("mozilla")); 145 | assertThat(httpClientMock.execute(getChrome), hasContent("chrome")); 146 | assertThat(httpClientMock.execute(getSafari), hasStatus(404)); 147 | } 148 | 149 | @Test 150 | public void should_match_right_parameter_value() throws IOException { 151 | HttpClientMock httpClientMock = new HttpClientMock("http://localhost:8080"); 152 | 153 | httpClientMock 154 | .onGet("/foo").withParameter("id", "1").withParameter("name", "abc") 155 | .doReturn("one"); 156 | httpClientMock 157 | .onGet("/foo").withParameter("id", "2") 158 | .doReturn("two"); 159 | 160 | HttpResponse one = httpClientMock.execute(new HttpGet("http://localhost:8080/foo?id=1&name=abc")); 161 | HttpResponse two = httpClientMock.execute(new HttpGet("http://localhost:8080/foo?id=2")); 162 | 163 | assertThat(one, hasContent("one")); 164 | assertThat(two, hasContent("two")); 165 | } 166 | 167 | @Test 168 | public void should_add_default_host_to_every_relative_path() throws IOException { 169 | HttpClientMock httpClientMock = new HttpClientMock("http://localhost:8080"); 170 | 171 | httpClientMock.onGet("/login").doReturn("login"); 172 | httpClientMock.onGet("/product/search").doReturn("search"); 173 | httpClientMock.onGet("/logout").doReturn("logout"); 174 | 175 | HttpResponse login = httpClientMock.execute(new HttpGet("http://localhost:8080/login")); 176 | HttpResponse search = httpClientMock.execute(new HttpGet("http://localhost:8080/product/search")); 177 | HttpResponse logout = httpClientMock.execute(new HttpGet("http://localhost:8080/logout")); 178 | 179 | assertThat(login, hasContent("login")); 180 | assertThat(search, hasContent("search")); 181 | assertThat(logout, hasContent("logout")); 182 | 183 | } 184 | 185 | @Test 186 | public void checkBody() throws IOException { 187 | HttpClientMock httpClientMock = new HttpClientMock("http://localhost:8080"); 188 | 189 | httpClientMock.onPost("/login") 190 | .doReturnStatus(500); 191 | httpClientMock.onPost("/login").withBody(containsString("foo")) 192 | .doReturnStatus(200); 193 | 194 | HttpResponse badLogin = httpClientMock.execute(new HttpPost("http://localhost:8080/login")); 195 | HttpResponse correctLogin = httpClientMock.execute(httpPost("http://localhost:8080/login", "foo")); 196 | 197 | assertThat(correctLogin, hasStatus(200)); 198 | assertThat(badLogin, hasStatus(500)); 199 | } 200 | 201 | @Test 202 | public void when_url_contains_parameter_it_should_be_added_us_a_separate_condition() throws IOException { 203 | HttpClientMock httpClientMock = new HttpClientMock("http://localhost"); 204 | 205 | httpClientMock.onPost("/login?user=john") 206 | .withoutExtraParameters() 207 | .doReturnStatus(400); 208 | httpClientMock.onPost("/login?user=john&pass=abc") 209 | .withoutExtraParameters() 210 | .doReturnStatus(200); 211 | 212 | HttpResponse notFound = httpClientMock.execute(new HttpPost("http://localhost/login")); 213 | HttpResponse wrong = httpClientMock.execute(new HttpPost("http://localhost/login?user=john")); 214 | HttpResponse ok = httpClientMock.execute(new HttpPost("http://localhost/login?user=john&pass=abc")); 215 | HttpResponse notFound_2 = httpClientMock.execute(new HttpPost("http://localhost/login?user=john&pass=abc&foo=bar")); 216 | 217 | assertThat(notFound, hasStatus(404)); 218 | assertThat(wrong, hasStatus(400)); 219 | assertThat(ok, hasStatus(200)); 220 | assertThat(notFound_2, hasStatus(404)); 221 | } 222 | 223 | @Test 224 | public void when_url_contains_reference_it_should_be_added_us_a_separate_condition() throws IOException { 225 | HttpClientMock httpClientMock = new HttpClientMock("http://localhost"); 226 | 227 | httpClientMock.onPost("/login") 228 | .doReturnStatus(400); 229 | httpClientMock.onPost("/login#abc") 230 | .doReturnStatus(200); 231 | 232 | HttpResponse wrong = httpClientMock.execute(new HttpPost("http://localhost/login")); 233 | HttpResponse ok = httpClientMock.execute(new HttpPost("http://localhost/login#abc")); 234 | 235 | assertThat(wrong, hasStatus(400)); 236 | assertThat(ok, hasStatus(200)); 237 | } 238 | 239 | @Test 240 | public void should_handle_path_with_parameters_and_reference() throws IOException { 241 | HttpClientMock httpClientMock = new HttpClientMock("http://localhost"); 242 | 243 | httpClientMock.onPost("/login?p=1#abc") 244 | .doReturnStatus(200); 245 | 246 | HttpResponse wrong1 = httpClientMock.execute(new HttpPost("http://localhost/login")); 247 | HttpResponse wrong2 = httpClientMock.execute(new HttpPost("http://localhost/login?p=1")); 248 | HttpResponse wrong3 = httpClientMock.execute(new HttpPost("http://localhost/login#abc")); 249 | HttpResponse ok = httpClientMock.execute(new HttpPost("http://localhost/login?p=1#abc")); 250 | 251 | assertThat(wrong1, hasStatus(404)); 252 | assertThat(wrong2, hasStatus(404)); 253 | assertThat(wrong3, hasStatus(404)); 254 | assertThat(ok, hasStatus(200)); 255 | } 256 | 257 | @Test 258 | public void should_check_reference_value() throws IOException { 259 | HttpClientMock httpClientMock = new HttpClientMock("http://localhost"); 260 | 261 | httpClientMock.onPost("/login") 262 | .doReturnStatus(400); 263 | httpClientMock.onPost("/login") 264 | .withReference("ref") 265 | .doReturnStatus(200); 266 | 267 | HttpResponse wrong = httpClientMock.execute(new HttpPost("http://localhost/login")); 268 | HttpResponse ok = httpClientMock.execute(new HttpPost("http://localhost/login#ref")); 269 | 270 | assertThat(wrong, hasStatus(400)); 271 | assertThat(ok, hasStatus(200)); 272 | } 273 | 274 | @Test 275 | public void after_reset_every_call_should_result_in_status_404() throws IOException { 276 | HttpClientMock httpClientMock = new HttpClientMock("http://localhost"); 277 | 278 | httpClientMock.onPost("/login").doReturnStatus(200); 279 | httpClientMock.reset(); 280 | HttpResponse login = httpClientMock.execute(new HttpPost("http://localhost/login")); 281 | 282 | assertThat(login, hasStatus(404)); 283 | } 284 | 285 | @Test 286 | public void after_execute_and_reset_every_call_should_result_in_status_404() throws IOException { 287 | HttpClientMock httpClientMock = new HttpClientMock("http://localhost"); 288 | 289 | httpClientMock.onPost("/login").doReturnStatus(200); 290 | httpClientMock.execute(new HttpPost("http://localhost/login")); 291 | httpClientMock.reset(); 292 | HttpResponse login = httpClientMock.execute(new HttpPost("http://localhost/login")); 293 | 294 | assertThat(login, hasStatus(404)); 295 | } 296 | 297 | @Test 298 | public void after_reset_number_of_calls_should_be_zero() throws IOException { 299 | HttpClientMock httpClientMock = new HttpClientMock("http://localhost"); 300 | 301 | httpClientMock.onPost("/login").doReturnStatus(200); 302 | httpClientMock.execute(new HttpPost("http://localhost/login")); 303 | httpClientMock.execute(new HttpPost("http://localhost/login")); 304 | httpClientMock.reset(); 305 | httpClientMock.verify().post("/login").notCalled(); 306 | 307 | httpClientMock.onPost("/login").doReturnStatus(200); 308 | httpClientMock.execute(new HttpPost("http://localhost/login")); 309 | httpClientMock.execute(new HttpPost("http://localhost/login")); 310 | httpClientMock.verify().post("/login").called(2); 311 | 312 | } 313 | 314 | @Test 315 | public void not_all_parameters_occurred() throws IOException { 316 | HttpClientMock httpClientMock = new HttpClientMock("http://localhost"); 317 | 318 | httpClientMock.onPost("/login") 319 | .withParameter("foo", "bar") 320 | .doReturnStatus(200); 321 | 322 | HttpResponse response = httpClientMock.execute(new HttpPost("http://localhost/login")); 323 | assertThat(response, hasStatus(404)); 324 | } 325 | 326 | @Test 327 | public void should_allow_different_host_then_default() throws IOException { 328 | HttpClientMock httpClientMock = new HttpClientMock("http://localhost"); 329 | 330 | httpClientMock.onGet("/login").doReturn("login"); 331 | httpClientMock.onGet("http://www.google.com").doReturn("google"); 332 | 333 | HttpResponse login = httpClientMock.execute(new HttpGet("http://localhost/login")); 334 | HttpResponse google = httpClientMock.execute(new HttpGet("http://www.google.com")); 335 | assertThat(login, hasContent("login")); 336 | assertThat(google, hasContent("google")); 337 | } 338 | 339 | @Test 340 | public void withFormParameter() throws IOException { 341 | HttpClientMock httpClientMock = new HttpClientMock("http://localhost"); 342 | 343 | httpClientMock.onPost("/login") 344 | .withFormParameter("username", "John") 345 | .withFormParameter("password", Matchers.containsString("secret")) 346 | .doReturnStatus(200); 347 | 348 | HttpPost request = new HttpPost("http://localhost/login"); 349 | request.setEntity(new UrlEncodedFormEntity(Arrays.asList( 350 | new BasicNameValuePair("username", "John"), 351 | new BasicNameValuePair("password", "secret!") 352 | ))); 353 | HttpResponse response = httpClientMock.execute(request); 354 | assertThat(response, hasStatus(200)); 355 | 356 | request = new HttpPost("http://localhost/login"); 357 | request.setEntity(new UrlEncodedFormEntity(Arrays.asList( 358 | new BasicNameValuePair("username", "John"), 359 | new BasicNameValuePair("password", "wrong") 360 | ))); 361 | response = httpClientMock.execute(request); 362 | assertThat(response, hasStatus(404)); 363 | } 364 | 365 | @Test 366 | public void withFormParameters() throws IOException { 367 | HttpClientMock httpClientMock = new HttpClientMock("http://localhost"); 368 | 369 | ParametersMatcher parameters = new ParametersMatcher(); 370 | parameters.put("username", Matchers.equalTo("John")); 371 | parameters.put("password", Matchers.containsString("secret")); 372 | 373 | httpClientMock.onPost("/login") 374 | .withFormParameters(parameters) 375 | .doReturnStatus(200); 376 | 377 | HttpPost request = new HttpPost("http://localhost/login"); 378 | request.setEntity(new UrlEncodedFormEntity(Arrays.asList( 379 | new BasicNameValuePair("username", "John"), 380 | new BasicNameValuePair("password", "secret!") 381 | ))); 382 | HttpResponse response = httpClientMock.execute(request); 383 | assertThat(response, hasStatus(200)); 384 | 385 | request = new HttpPost("http://localhost/login"); 386 | request.setEntity(new UrlEncodedFormEntity(Arrays.asList( 387 | new BasicNameValuePair("username", "John"), 388 | new BasicNameValuePair("password", "wrong") 389 | ))); 390 | response = httpClientMock.execute(request); 391 | assertThat(response, hasStatus(404)); 392 | } 393 | 394 | @Test 395 | public void withFormParameter_should_match_when_allParametersHaveMatchingValue() throws IOException { 396 | HttpClientMock httpClientMock = new HttpClientMock("http://localhost"); 397 | 398 | httpClientMock.onPost("/login") 399 | .withFormParameter("username", "John") 400 | .withFormParameter("password", Matchers.containsString("secret")) 401 | .doReturnStatus(200); 402 | 403 | HttpPost request = new HttpPost("http://localhost/login"); 404 | request.setEntity(new UrlEncodedFormEntity(Arrays.asList( 405 | new BasicNameValuePair("username", "John"), 406 | new BasicNameValuePair("password", "secret!") 407 | ))); 408 | HttpResponse response = httpClientMock.execute(request); 409 | assertThat(response, hasStatus(200)); 410 | } 411 | 412 | @Test 413 | public void withFormParameters_should_match_when_allParametersHaveMatchingValue() throws IOException { 414 | HttpClientMock httpClientMock = new HttpClientMock("http://localhost"); 415 | 416 | ParametersMatcher parameters = new ParametersMatcher(); 417 | parameters.put("username", Matchers.equalTo("John")); 418 | parameters.put("password", Matchers.containsString("secret")); 419 | 420 | httpClientMock.onPost("/login") 421 | .withFormParameters(parameters) 422 | .doReturnStatus(200); 423 | 424 | HttpPost request = new HttpPost("http://localhost/login"); 425 | request.setEntity(new UrlEncodedFormEntity(Arrays.asList( 426 | new BasicNameValuePair("username", "John"), 427 | new BasicNameValuePair("password", "secret!") 428 | ))); 429 | HttpResponse response = httpClientMock.execute(request); 430 | assertThat(response, hasStatus(200)); 431 | 432 | httpClientMock.debugOn(); 433 | request = new HttpPost("http://localhost/login"); 434 | request.setEntity(new UrlEncodedFormEntity(Arrays.asList( 435 | new BasicNameValuePair("username", "John"), 436 | new BasicNameValuePair("password", "wrong") 437 | ))); 438 | response = httpClientMock.execute(request); 439 | assertThat(response, hasStatus(404)); 440 | } 441 | 442 | @Test 443 | public void withFormParameters_should_notMatch_when_formParameterHasNotMatchingValue() throws IOException { 444 | HttpClientMock httpClientMock = new HttpClientMock("http://localhost"); 445 | httpClientMock.onPost("/login") 446 | .withFormParameter("username", "John") 447 | .withFormParameter("password", "secret") 448 | .doReturnStatus(200); 449 | 450 | HttpPost request = new HttpPost("http://localhost/login"); 451 | request.setEntity(new UrlEncodedFormEntity(Arrays.asList( 452 | new BasicNameValuePair("username", "John"), 453 | new BasicNameValuePair("password", "wrong") 454 | ))); 455 | HttpResponse response = httpClientMock.execute(request); 456 | assertThat(response, hasStatus(404)); 457 | } 458 | 459 | @Test 460 | public void withFormParameter_should_notMatch_when_formParameterIsMissing() throws IOException { 461 | HttpClientMock httpClientMock = new HttpClientMock("http://localhost"); 462 | httpClientMock.onPost("/login") 463 | .withFormParameter("username", "John") 464 | .withFormParameter("password", "secret") 465 | .doReturnStatus(200); 466 | 467 | HttpPost request = new HttpPost("http://localhost/login"); 468 | request.setEntity(new UrlEncodedFormEntity(Collections.singleton( 469 | new BasicNameValuePair("username", "John") 470 | ))); 471 | HttpResponse response = httpClientMock.execute(request); 472 | assertThat(response, hasStatus(404)); 473 | } 474 | 475 | @Test 476 | public void withFormParameter_should_notMatch_when_extraParameterIsPresent() throws IOException { 477 | HttpClientMock httpClientMock = new HttpClientMock("http://localhost"); 478 | httpClientMock.debugOn(); 479 | httpClientMock.onPost("/login") 480 | .withFormParameter("username", "John") 481 | .withFormParameter("password", "secret") 482 | .withoutExtraFormParameters() 483 | .doReturnStatus(200); 484 | 485 | HttpPost request = new HttpPost("http://localhost/login"); 486 | request.setEntity(new UrlEncodedFormEntity(Arrays.asList( 487 | new BasicNameValuePair("username", "John"), 488 | new BasicNameValuePair("password", "secret"), 489 | new BasicNameValuePair("extra", "foo") 490 | ))); 491 | HttpResponse response = httpClientMock.execute(request); 492 | assertThat(response, hasStatus(404)); 493 | } 494 | 495 | @Test 496 | public void withFormParameter_should_Match_when_extraParameterIsPresentAndAllowExtraParametersIsTrue() throws IOException { 497 | HttpClientMock httpClientMock = new HttpClientMock("http://localhost"); 498 | httpClientMock.debugOn(); 499 | httpClientMock.onPost("/login") 500 | .withFormParameter("username", "John") 501 | .withFormParameter("password", "secret") 502 | .withExtraFormParameters() 503 | .doReturnStatus(200); 504 | 505 | HttpPost request = new HttpPost("http://localhost/login"); 506 | request.setEntity(new UrlEncodedFormEntity(Arrays.asList( 507 | new BasicNameValuePair("username", "John"), 508 | new BasicNameValuePair("password", "secret"), 509 | new BasicNameValuePair("wrong", "bad") 510 | ))); 511 | HttpResponse response = httpClientMock.execute(request); 512 | assertThat(response, hasStatus(200)); 513 | } 514 | 515 | @Test 516 | public void should_notmatch_whenRequestHasExtraParametersAndOnPostWithoutUri() throws IOException { 517 | HttpClientMock httpClientMock = new HttpClientMock("http://localhost"); 518 | 519 | httpClientMock.onPost() 520 | .withPath("/login") 521 | .withoutExtraParameters() 522 | .doReturnStatus(200); 523 | 524 | HttpResponse response = httpClientMock.execute(new HttpPost("http://localhost/login?foo=bar")); 525 | assertThat(response, hasStatus(404)); 526 | } 527 | 528 | @Test 529 | public void should_notMatch_whenRequestHasExtraParametersAndOnPostWithUri() throws IOException { 530 | HttpClientMock httpClientMock = new HttpClientMock("http://localhost"); 531 | httpClientMock.onPost("/login") 532 | .withoutExtraParameters() 533 | .doReturnStatus(200); 534 | 535 | HttpResponse response = httpClientMock.execute(new HttpPost("http://localhost/login?foo=bar")); 536 | assertThat(response, hasStatus(404)); 537 | } 538 | 539 | @Test 540 | public void should_match_whenRequestHasExtraParametersAndAllowExtraParametersIsTrue() throws IOException { 541 | HttpClientMock httpClientMock = new HttpClientMock("http://localhost"); 542 | 543 | httpClientMock.onPost("/login") 544 | .withExtraParameters() 545 | .doReturnStatus(200); 546 | 547 | HttpResponse response = httpClientMock.execute(new HttpPost("http://localhost/login?foo=bar")); 548 | assertThat(response, hasStatus(200)); 549 | } 550 | 551 | @Test 552 | public void should_notMatch_whenRequestHasExtraParametersAndAllowExtraParametersIsFalse() throws IOException { 553 | HttpClientMock httpClientMock = new HttpClientMock("http://localhost"); 554 | 555 | httpClientMock.onPost() 556 | .withPath("/login") 557 | .withoutExtraParameters() 558 | .doReturnStatus(200); 559 | 560 | HttpResponse response = httpClientMock.execute(new HttpPost("http://localhost/login?foo=bar")); 561 | assertThat(response, hasStatus(404)); 562 | } 563 | 564 | @Test 565 | public void should_notMatch_whenRequestHasExtraFormParametersAndAllowExtraFormParametersIsFalse() throws IOException { 566 | HttpClientMock httpClientMock = new HttpClientMock("http://localhost"); 567 | 568 | httpClientMock.onPost() 569 | .withPath("/login") 570 | .withoutExtraFormParameters() 571 | .doReturnStatus(200); 572 | httpClientMock.onPost("/login") 573 | .withoutExtraFormParameters() 574 | .doReturnStatus(200); 575 | 576 | HttpPost request = new HttpPost("http://localhost/login"); 577 | request.setEntity(new UrlEncodedFormEntity(Arrays.asList( 578 | new BasicNameValuePair("username", "John") 579 | ))); 580 | HttpResponse response = httpClientMock.execute(request); 581 | assertThat(response, hasStatus(404)); 582 | } 583 | 584 | @Test 585 | public void should_work_with_non_absolute_uri() throws IOException { 586 | HttpClientMock httpClientMock = new HttpClientMock(); 587 | httpClientMock.onGet().doReturn("ok"); 588 | httpClientMock.onGet().withPath("/foo").doReturn("foo"); 589 | HttpResponse ok = httpClientMock.execute(new HttpHost("localhost"), new HttpGet("/")); 590 | HttpResponse foo = httpClientMock.execute(new HttpHost("localhost"), new HttpGet("/foo")); 591 | assertThat(ok, hasContent("ok")); 592 | assertThat(foo, hasContent("foo")); 593 | } 594 | 595 | } 596 | 597 | 598 | -------------------------------------------------------------------------------- /src/test/java/com/github/paweladamski/httpclientmock/HttpClientMockBuilder_doReturnStatusTest.java: -------------------------------------------------------------------------------- 1 | package com.github.paweladamski.httpclientmock; 2 | 3 | import static com.github.paweladamski.httpclientmock.Requests.httpGet; 4 | import static com.github.paweladamski.httpclientmock.matchers.HttpResponseMatchers.hasContent; 5 | import static com.github.paweladamski.httpclientmock.matchers.HttpResponseMatchers.hasStatus; 6 | import static org.hamcrest.MatcherAssert.assertThat; 7 | 8 | import java.io.IOException; 9 | import org.apache.http.client.methods.CloseableHttpResponse; 10 | import org.junit.jupiter.params.ParameterizedTest; 11 | import org.junit.jupiter.params.provider.ValueSource; 12 | 13 | public class HttpClientMockBuilder_doReturnStatusTest { 14 | 15 | @ParameterizedTest 16 | @ValueSource(ints = {200, 300, 400, 500}) 17 | public void doReturnStatus_should_set_response_status_and_empty_entity(int statusCode) throws IOException { 18 | HttpClientMock httpClientMock = new HttpClientMock(); 19 | httpClientMock.onGet().doReturnStatus(statusCode); 20 | 21 | CloseableHttpResponse response = httpClientMock.execute(httpGet("http://localhost")); 22 | assertThat(response, hasStatus(statusCode)); 23 | assertThat(response, hasContent("")); 24 | } 25 | 26 | } -------------------------------------------------------------------------------- /src/test/java/com/github/paweladamski/httpclientmock/HttpClientMockBuilder_doReturnWithStatusTest.java: -------------------------------------------------------------------------------- 1 | package com.github.paweladamski.httpclientmock; 2 | 3 | import static com.github.paweladamski.httpclientmock.Requests.httpGet; 4 | import static com.github.paweladamski.httpclientmock.matchers.HttpResponseMatchers.hasNoEntity; 5 | import static com.github.paweladamski.httpclientmock.matchers.HttpResponseMatchers.hasStatus; 6 | import static org.hamcrest.MatcherAssert.assertThat; 7 | import static org.hamcrest.Matchers.equalTo; 8 | 9 | import java.io.IOException; 10 | import org.apache.http.HttpResponse; 11 | import org.apache.http.client.methods.CloseableHttpResponse; 12 | import org.junit.jupiter.params.ParameterizedTest; 13 | import org.junit.jupiter.params.provider.ValueSource; 14 | 15 | public class HttpClientMockBuilder_doReturnWithStatusTest { 16 | 17 | @ParameterizedTest 18 | @ValueSource(ints = {200, 300, 400, 500}) 19 | public void doReturn_should_set_response_status_empty_reason_and_null_entity_when_only_statusCode_is_provided(int statusCode) throws IOException { 20 | HttpClientMock httpClientMock = new HttpClientMock(); 21 | httpClientMock.onGet().doReturnWithStatus(statusCode); 22 | 23 | CloseableHttpResponse response = httpClientMock.execute(httpGet("http://localhost")); 24 | assertThat(response, hasStatus(statusCode)); 25 | assertThat(response, hasNoEntity()); 26 | } 27 | 28 | @ParameterizedTest 29 | @ValueSource(ints = {200, 300, 400, 500}) 30 | public void doReturn_should_set_response_status_and_reason_phrase(int statusCode) throws IOException { 31 | HttpClientMock httpClientMock = new HttpClientMock(); 32 | httpClientMock.onGet().doReturnWithStatus(statusCode, "reason"); 33 | 34 | HttpResponse response = httpClientMock.execute(httpGet("http://localhost")); 35 | assertThat(response, hasStatus(statusCode)); 36 | assertThat(response.getStatusLine().getReasonPhrase(), equalTo("reason")); 37 | } 38 | 39 | } -------------------------------------------------------------------------------- /src/test/java/com/github/paweladamski/httpclientmock/HttpClientMockTest.java: -------------------------------------------------------------------------------- 1 | package com.github.paweladamski.httpclientmock; 2 | 3 | import static com.github.paweladamski.httpclientmock.matchers.HttpResponseMatchers.hasStatus; 4 | import static org.hamcrest.MatcherAssert.assertThat; 5 | import static org.hamcrest.Matchers.equalTo; 6 | 7 | import java.io.IOException; 8 | import org.apache.http.HttpResponse; 9 | import org.apache.http.client.methods.HttpGet; 10 | import org.junit.jupiter.api.Test; 11 | 12 | public class HttpClientMockTest { 13 | 14 | @Test 15 | public void should_run_requestInterceptors() throws IOException { 16 | HttpClientMock httpClientMock = new HttpClientMock(); 17 | httpClientMock.addRequestInterceptor((request, context) -> request.addHeader("foo", "bar")); 18 | httpClientMock.onGet().withHeader("foo", "bar").doReturn("ok"); 19 | 20 | HttpResponse ok = httpClientMock.execute(new HttpGet("http://localhost")); 21 | assertThat(ok, hasStatus(200)); 22 | } 23 | 24 | @Test 25 | public void should_run_responseInterceptors() throws IOException { 26 | HttpClientMock httpClientMock = new HttpClientMock(); 27 | httpClientMock.addResponseInterceptor((request, context) -> request.addHeader("foo", "bar")); 28 | httpClientMock.onGet().doReturn("ok"); 29 | 30 | HttpResponse ok = httpClientMock.execute(new HttpGet("http://localhost")); 31 | assertThat(ok.getFirstHeader("foo").getValue(), equalTo("bar")); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/test/java/com/github/paweladamski/httpclientmock/HttpClientResponseBuilderTest.java: -------------------------------------------------------------------------------- 1 | package com.github.paweladamski.httpclientmock; 2 | 3 | import static com.github.paweladamski.httpclientmock.Asserts.assertThrows; 4 | import static com.github.paweladamski.httpclientmock.Requests.httpGet; 5 | import static com.github.paweladamski.httpclientmock.Requests.httpPost; 6 | import static com.github.paweladamski.httpclientmock.matchers.HttpResponseMatchers.hasContent; 7 | import static com.github.paweladamski.httpclientmock.matchers.HttpResponseMatchers.hasCookie; 8 | import static com.github.paweladamski.httpclientmock.matchers.HttpResponseMatchers.hasReason; 9 | import static com.github.paweladamski.httpclientmock.matchers.HttpResponseMatchers.hasStatus; 10 | import static org.apache.http.HttpStatus.SC_NOT_FOUND; 11 | import static org.apache.http.HttpStatus.SC_NO_CONTENT; 12 | import static org.apache.http.entity.ContentType.APPLICATION_JSON; 13 | import static org.apache.http.entity.ContentType.APPLICATION_XML; 14 | import static org.hamcrest.MatcherAssert.assertThat; 15 | import static org.hamcrest.Matchers.equalTo; 16 | import static org.junit.jupiter.api.Assertions.assertEquals; 17 | import static org.junit.jupiter.api.Assertions.assertNull; 18 | 19 | import com.github.paweladamski.httpclientmock.action.Action; 20 | import java.io.IOException; 21 | import java.nio.charset.Charset; 22 | import java.nio.charset.StandardCharsets; 23 | import java.util.Arrays; 24 | import java.util.Collections; 25 | import java.util.List; 26 | import org.apache.http.HttpEntity; 27 | import org.apache.http.HttpResponse; 28 | import org.apache.http.NameValuePair; 29 | import org.apache.http.ProtocolVersion; 30 | import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; 31 | import org.apache.http.client.methods.HttpGet; 32 | import org.apache.http.client.protocol.HttpClientContext; 33 | import org.apache.http.client.utils.URLEncodedUtils; 34 | import org.apache.http.entity.ContentType; 35 | import org.apache.http.message.BasicHttpResponse; 36 | import org.apache.http.message.BasicNameValuePair; 37 | import org.junit.jupiter.api.Assertions; 38 | import org.junit.jupiter.api.Test; 39 | 40 | public class HttpClientResponseBuilderTest { 41 | 42 | @Test 43 | public void should_return_status_404_when_no_rule_matches() throws IOException { 44 | HttpClientMock httpClientMock = new HttpClientMock(); 45 | HttpResponse notFound = httpClientMock.execute(new HttpGet("http://localhost/foo")); 46 | assertThat(notFound, hasStatus(404)); 47 | } 48 | 49 | @Test 50 | public void should_use_next_action_after_every_call() throws Exception { 51 | HttpClientMock httpClientMock = new HttpClientMock("http://localhost"); 52 | 53 | httpClientMock.onGet("/foo") 54 | .doReturn("first") 55 | .doReturn("second") 56 | .doReturn("third"); 57 | 58 | httpClientMock.onGet("/bar") 59 | .doReturn("bar") 60 | .doReturnStatus(300) 61 | .doThrowException(new IOException()); 62 | 63 | HttpResponse response1 = httpClientMock.execute(new HttpGet("http://localhost/foo")); 64 | HttpResponse response2 = httpClientMock.execute(new HttpGet("http://localhost/foo")); 65 | HttpResponse response3 = httpClientMock.execute(new HttpGet("http://localhost/foo")); 66 | HttpResponse response4 = httpClientMock.execute(new HttpGet("http://localhost/foo")); 67 | HttpResponse response5 = httpClientMock.execute(new HttpGet("http://localhost/foo")); 68 | 69 | assertThat(response1, hasContent("first")); 70 | assertThat(response2, hasContent("second")); 71 | assertThat(response3, hasContent("third")); 72 | assertThat(response4, hasContent("third")); 73 | assertThat(response5, hasContent("third")); 74 | 75 | HttpResponse bar1 = httpClientMock.execute(new HttpGet("http://localhost/bar")); 76 | HttpResponse bar2 = httpClientMock.execute(new HttpGet("http://localhost/bar")); 77 | assertThat(bar1, hasContent("bar")); 78 | assertThat(bar2, hasStatus(300)); 79 | 80 | assertThrows(IOException.class, () -> httpClientMock.execute(new HttpGet("http://localhost/bar"))); 81 | 82 | } 83 | 84 | @Test 85 | public void should_support_response_in_different_charsets() throws IOException { 86 | HttpClientMock httpClientMock = new HttpClientMock("http://localhost"); 87 | 88 | httpClientMock.onGet("/foo") 89 | .doReturn("first") 90 | .doReturn("second", StandardCharsets.UTF_16) 91 | .doReturn("third", StandardCharsets.US_ASCII); 92 | 93 | HttpResponse response1 = httpClientMock.execute(new HttpGet("http://localhost/foo")); 94 | HttpResponse response2 = httpClientMock.execute(new HttpGet("http://localhost/foo")); 95 | HttpResponse response3 = httpClientMock.execute(new HttpGet("http://localhost/foo")); 96 | 97 | assertThat(response1, hasContent("first", "UTF-8")); 98 | assertThat(response2, hasContent("second", "UTF-16")); 99 | assertThat(response3, hasContent("third", "ASCII")); 100 | } 101 | 102 | @Test 103 | public void should_support_response_with_different_contentType() throws IOException { 104 | HttpClientMock httpClientMock = new HttpClientMock("http://localhost:8080"); 105 | httpClientMock 106 | .onGet("/json").doReturn("{\"a\":1}", Charset.defaultCharset(), APPLICATION_JSON); 107 | httpClientMock 108 | .onGet("/xml").doReturn("1", Charset.defaultCharset(), APPLICATION_XML); 109 | 110 | HttpResponse jsonResponse = httpClientMock.execute(httpGet("http://localhost:8080/json")); 111 | HttpResponse xmlResponse = httpClientMock.execute(httpGet("http://localhost:8080/xml")); 112 | 113 | assertThat(jsonResponse, hasContent("{\"a\":1}")); 114 | assertThat(jsonResponse.getFirstHeader("Content-type").getValue(), equalTo(APPLICATION_JSON.toString())); 115 | assertThat(ContentType.get(jsonResponse.getEntity()).toString(), equalTo(APPLICATION_JSON.toString())); 116 | 117 | assertThat(xmlResponse, hasContent("1")); 118 | assertThat(xmlResponse.getFirstHeader("Content-type").getValue(), equalTo(APPLICATION_XML.toString())); 119 | assertThat(ContentType.get(xmlResponse.getEntity()).toString(), equalTo(APPLICATION_XML.toString())); 120 | 121 | } 122 | 123 | @Test 124 | public void should_support_response_in_body_with_status() throws IOException { 125 | HttpClientMock httpClientMock = new HttpClientMock("http://localhost"); 126 | 127 | httpClientMock.onGet("/foo") 128 | .doReturn("first") 129 | .doReturn(300, "second") 130 | .doReturn(400, "third"); 131 | 132 | HttpResponse response1 = httpClientMock.execute(new HttpGet("http://localhost/foo")); 133 | HttpResponse response2 = httpClientMock.execute(new HttpGet("http://localhost/foo")); 134 | HttpResponse response3 = httpClientMock.execute(new HttpGet("http://localhost/foo")); 135 | HttpResponse response4 = httpClientMock.execute(new HttpGet("http://localhost/foo")); 136 | HttpResponse response5 = httpClientMock.execute(new HttpGet("http://localhost/foo")); 137 | 138 | assertThat(response1, hasContent("first")); 139 | assertThat(response1, hasStatus(200)); 140 | assertThat(response2, hasContent("second")); 141 | assertThat(response2, hasStatus(300)); 142 | assertThat(response3, hasContent("third")); 143 | assertThat(response3, hasStatus(400)); 144 | assertThat(response4, hasContent("third")); 145 | assertThat(response4, hasStatus(400)); 146 | assertThat(response5, hasContent("third")); 147 | assertThat(response5, hasStatus(400)); 148 | } 149 | 150 | @Test 151 | public void should_throw_exception_when_throwing_action_matched() throws IOException { 152 | HttpClientMock httpClientMock = new HttpClientMock("http://localhost:8080"); 153 | httpClientMock.onGet("/foo").doThrowException(new IOException()); 154 | Assertions.assertThrows( 155 | IOException.class, 156 | ()-> httpClientMock.execute(new HttpGet("http://localhost:8080/foo")) 157 | ); 158 | } 159 | 160 | @Test 161 | public void should_return_status_corresponding_to_match() throws IOException { 162 | HttpClientMock httpClientMock = new HttpClientMock("http://localhost:8080"); 163 | 164 | httpClientMock.onGet("/login").doReturnStatus(200); 165 | httpClientMock.onGet("/abc").doReturnStatus(404); 166 | httpClientMock.onGet("/error").doReturnStatus(500); 167 | 168 | HttpResponse ok = httpClientMock.execute(new HttpGet("http://localhost:8080/login")); 169 | HttpResponse notFound = httpClientMock.execute(new HttpGet("http://localhost:8080/abc")); 170 | HttpResponse error = httpClientMock.execute(new HttpGet("http://localhost:8080/error")); 171 | 172 | assertThat(ok, hasStatus(200)); 173 | assertThat(notFound, hasStatus(404)); 174 | assertThat(error, hasStatus(500)); 175 | 176 | } 177 | 178 | @Test 179 | public void should_do_custom_action() throws IOException { 180 | HttpClientMock httpClientMock = new HttpClientMock("http://localhost:8080"); 181 | httpClientMock.onPost("/login").doAction(echo()); 182 | HttpResponse response = httpClientMock.execute(httpPost("http://localhost:8080/login", "foo bar")); 183 | 184 | assertThat(response, hasContent("foo bar")); 185 | 186 | } 187 | 188 | @Test 189 | public void should_add_header_to_response() throws IOException { 190 | HttpClientMock httpClientMock = new HttpClientMock("http://localhost:8080"); 191 | httpClientMock.onPost("/login") 192 | .doReturn("foo").withHeader("tracking", "123") 193 | .doReturn("foo").withHeader("tracking", "456"); 194 | 195 | HttpResponse first = httpClientMock.execute(httpPost("http://localhost:8080/login")); 196 | HttpResponse second = httpClientMock.execute(httpPost("http://localhost:8080/login")); 197 | 198 | assertThat(first.getFirstHeader("tracking").getValue(), equalTo("123")); 199 | assertThat(second.getFirstHeader("tracking").getValue(), equalTo("456")); 200 | } 201 | 202 | @Test 203 | public void should_add_cookie_to_context() throws IOException { 204 | HttpClientMock httpClientMock = new HttpClientMock("http://localhost:8080"); 205 | httpClientMock.onPost("/login") 206 | .doReturn("foo").withCookie("cookieName", "cookieValue") 207 | .doReturn("foo").withCookie("cookieName", "cookieValue2"); 208 | 209 | HttpClientContext httpClientContext = new HttpClientContext(); 210 | httpClientMock.execute(httpPost("http://localhost:8080/login"), httpClientContext); 211 | assertThat(httpClientContext, hasCookie("cookieName", "cookieValue")); 212 | 213 | httpClientMock.execute(httpPost("http://localhost:8080/login"), httpClientContext); 214 | assertThat(httpClientContext, hasCookie("cookieName", "cookieValue2")); 215 | } 216 | 217 | @Test 218 | public void should_add_status_to_response() throws IOException { 219 | HttpClientMock httpClientMock = new HttpClientMock("http://localhost:8080"); 220 | httpClientMock.onGet("/login") 221 | .doReturn("foo").withStatus(300); 222 | HttpResponse login = httpClientMock.execute(httpGet("http://localhost:8080/login")); 223 | 224 | assertThat(login, hasContent("foo")); 225 | assertThat(login, hasStatus(300)); 226 | 227 | } 228 | 229 | @Test 230 | public void should_add_status_and_reason_to_response() throws IOException { 231 | HttpClientMock httpClientMock = new HttpClientMock("http://localhost:8080"); 232 | httpClientMock.onGet("/login") 233 | .doReturn("foo").withStatus(400, "Bad request"); 234 | HttpResponse login = httpClientMock.execute(httpGet("http://localhost:8080/login")); 235 | 236 | assertThat(login, hasContent("foo")); 237 | assertThat(login, hasStatus(400)); 238 | assertThat(login, hasReason("Bad request")); 239 | } 240 | 241 | @Test 242 | public void should_return_json_with_right_header() throws IOException { 243 | HttpClientMock httpClientMock = new HttpClientMock("http://localhost:8080"); 244 | httpClientMock.onGet("/login") 245 | .doReturnJSON("{foo:1}", StandardCharsets.UTF_8); 246 | HttpResponse login = httpClientMock.execute(httpGet("http://localhost:8080/login")); 247 | 248 | assertThat(login, hasContent("{foo:1}")); 249 | assertThat(login.getFirstHeader("Content-type").getValue(), equalTo(APPLICATION_JSON.toString())); 250 | } 251 | 252 | @Test 253 | public void should_return_json_with_right_content_type() throws IOException { 254 | HttpClientMock httpClientMock = new HttpClientMock("http://localhost:8080"); 255 | httpClientMock.onGet("/login") 256 | .doReturnJSON("{foo:1}", StandardCharsets.UTF_8); 257 | HttpResponse login = httpClientMock.execute(httpGet("http://localhost:8080/login")); 258 | 259 | assertThat(login, hasContent("{foo:1}")); 260 | assertThat(ContentType.get(login.getEntity()).toString(), equalTo(APPLICATION_JSON.toString())); 261 | } 262 | 263 | @Test 264 | public void should_return_xml_with_right_header() throws IOException { 265 | HttpClientMock httpClientMock = new HttpClientMock("http://localhost:8080"); 266 | httpClientMock.onGet("/login") 267 | .doReturnXML("bar", StandardCharsets.UTF_8); 268 | HttpResponse login = httpClientMock.execute(httpGet("http://localhost:8080/login")); 269 | 270 | assertThat(login, hasContent("bar")); 271 | assertThat(login.getFirstHeader("Content-type").getValue(), equalTo(APPLICATION_XML.toString())); 272 | } 273 | 274 | @Test 275 | public void should_return_xml_with_right_content_type() throws IOException { 276 | HttpClientMock httpClientMock = new HttpClientMock("http://localhost:8080"); 277 | httpClientMock.onGet("/login") 278 | .doReturnXML("bar", StandardCharsets.UTF_8); 279 | HttpResponse login = httpClientMock.execute(httpGet("http://localhost:8080/login")); 280 | 281 | assertThat(login, hasContent("bar")); 282 | assertThat(ContentType.get(login.getEntity()).toString(), equalTo(APPLICATION_XML.toString())); 283 | } 284 | 285 | @Test 286 | public void should_not_set_response_entity_when_status_is_no_content() throws IOException { 287 | HttpClientMock httpClientMock = new HttpClientMock("http://localhost:8080"); 288 | httpClientMock.onGet("/login") 289 | .doReturnStatus(SC_NO_CONTENT); 290 | 291 | HttpResponse login = httpClientMock.execute(httpGet("http://localhost:8080/login")); 292 | 293 | assertNull(login.getEntity()); 294 | } 295 | 296 | @Test 297 | public void should_not_throw_exception_when_body_matcher_is_present_on_post_request() throws IOException { 298 | HttpClientMock httpClientMock = new HttpClientMock("http://localhost:8080"); 299 | httpClientMock.onPost("/path1") 300 | .withBody(equalTo("Body content")) 301 | .doReturnStatus(200); 302 | 303 | HttpResponse response = httpClientMock.execute(httpGet("http://localhost:8080/path2")); 304 | assertThat(response, hasStatus(SC_NOT_FOUND)); 305 | } 306 | 307 | @Test 308 | public void doReturnFormParams_should_returnResponseEntityWithFormParameters() throws IOException { 309 | HttpClientMock httpClientMock = new HttpClientMock("http://localhost:8080"); 310 | 311 | List expected = Arrays.asList( 312 | new BasicNameValuePair("one", "1"), 313 | new BasicNameValuePair("two", "2") 314 | ); 315 | httpClientMock.onGet("/path1").doReturnFormParams(expected); 316 | 317 | HttpResponse response = httpClientMock.execute(httpGet("http://localhost:8080/path1")); 318 | List actual = URLEncodedUtils.parse(response.getEntity()); 319 | 320 | assertThat(response, hasStatus(200)); 321 | assertEquals(expected, actual); 322 | } 323 | 324 | @Test 325 | public void doReturnFormParams_empty() throws IOException { 326 | HttpClientMock httpClientMock = new HttpClientMock("http://localhost:8080"); 327 | 328 | List expected = Collections.emptyList(); 329 | httpClientMock.onGet("/path1").doReturnFormParams(expected); 330 | 331 | HttpResponse response = httpClientMock.execute(httpGet("http://localhost:8080/path1")); 332 | List actual = URLEncodedUtils.parse(response.getEntity()); 333 | 334 | assertThat(response, hasStatus(200)); 335 | assertEquals(expected, actual); 336 | } 337 | 338 | private Action echo() { 339 | return r -> { 340 | HttpEntity entity = ((HttpEntityEnclosingRequestBase) r.getHttpRequest()).getEntity(); 341 | BasicHttpResponse response = new BasicHttpResponse(new ProtocolVersion("http", 1, 1), 200, "ok"); 342 | response.setEntity(entity); 343 | return response; 344 | }; 345 | } 346 | } 347 | 348 | 349 | -------------------------------------------------------------------------------- /src/test/java/com/github/paweladamski/httpclientmock/HttpClientVerifyTest.java: -------------------------------------------------------------------------------- 1 | package com.github.paweladamski.httpclientmock; 2 | 3 | import static com.github.paweladamski.httpclientmock.Requests.httpPost; 4 | import static com.github.paweladamski.httpclientmock.Requests.httpPut; 5 | import static org.hamcrest.Matchers.containsString; 6 | import static org.hamcrest.Matchers.greaterThanOrEqualTo; 7 | 8 | import com.github.paweladamski.httpclientmock.matchers.ParametersMatcher; 9 | import java.io.IOException; 10 | import java.util.Arrays; 11 | import org.apache.http.client.entity.UrlEncodedFormEntity; 12 | import org.apache.http.client.methods.HttpDelete; 13 | import org.apache.http.client.methods.HttpGet; 14 | import org.apache.http.client.methods.HttpHead; 15 | import org.apache.http.client.methods.HttpOptions; 16 | import org.apache.http.client.methods.HttpPatch; 17 | import org.apache.http.client.methods.HttpPost; 18 | import org.apache.http.client.methods.HttpPut; 19 | import org.apache.http.message.BasicNameValuePair; 20 | import org.hamcrest.Matchers; 21 | import org.junit.jupiter.api.Assertions; 22 | import org.junit.jupiter.api.Test; 23 | 24 | public class HttpClientVerifyTest { 25 | 26 | @Test 27 | public void shouldHandleAllHttpMethods() throws IOException { 28 | 29 | HttpClientMock httpClientMock = new HttpClientMock(); 30 | 31 | httpClientMock.execute(new HttpGet("http://localhost")); 32 | httpClientMock.execute(new HttpPost("http://localhost")); 33 | httpClientMock.execute(new HttpDelete("http://localhost")); 34 | httpClientMock.execute(new HttpPut("http://localhost")); 35 | httpClientMock.execute(new HttpHead("http://localhost")); 36 | httpClientMock.execute(new HttpOptions("http://localhost")); 37 | httpClientMock.execute(new HttpPatch("http://localhost")); 38 | 39 | httpClientMock.verify() 40 | .get("http://localhost") 41 | .called(); 42 | httpClientMock.verify() 43 | .post("http://localhost") 44 | .called(); 45 | httpClientMock.verify() 46 | .delete("http://localhost") 47 | .called(); 48 | httpClientMock.verify() 49 | .put("http://localhost") 50 | .called(); 51 | httpClientMock.verify() 52 | .options("http://localhost") 53 | .called(); 54 | httpClientMock.verify() 55 | .head("http://localhost") 56 | .called(); 57 | httpClientMock.verify() 58 | .patch("http://localhost") 59 | .called(); 60 | } 61 | 62 | @Test 63 | public void shouldCountNumberOfHttpMethodCalls() throws IOException { 64 | HttpClientMock httpClientMock = new HttpClientMock(); 65 | 66 | httpClientMock.execute(new HttpGet("http://localhost")); 67 | 68 | httpClientMock.execute(new HttpPost("http://localhost")); 69 | httpClientMock.execute(new HttpPost("http://localhost")); 70 | 71 | httpClientMock.execute(new HttpDelete("http://localhost")); 72 | httpClientMock.execute(new HttpDelete("http://localhost")); 73 | httpClientMock.execute(new HttpDelete("http://localhost")); 74 | 75 | httpClientMock.verify() 76 | .get("http://localhost") 77 | .called(); 78 | httpClientMock.verify() 79 | .post("http://localhost") 80 | .called(2); 81 | httpClientMock.verify() 82 | .delete("http://localhost") 83 | .called(3); 84 | 85 | httpClientMock.verify().get().called(greaterThanOrEqualTo(1)); 86 | httpClientMock.verify().post().called(greaterThanOrEqualTo(1)); 87 | httpClientMock.verify().delete().called(greaterThanOrEqualTo(1)); 88 | } 89 | 90 | @Test 91 | public void shouldCountNumberOfUrlCalls() throws IOException { 92 | HttpClientMock httpClientMock = new HttpClientMock(); 93 | 94 | httpClientMock.execute(new HttpGet("http://localhost")); 95 | 96 | httpClientMock.execute(new HttpGet("http://www.google.com")); 97 | httpClientMock.execute(new HttpGet("http://www.google.com")); 98 | 99 | httpClientMock.execute(new HttpGet("http://example.com")); 100 | httpClientMock.execute(new HttpGet("http://example.com")); 101 | httpClientMock.execute(new HttpGet("http://example.com")); 102 | 103 | httpClientMock.verify() 104 | .get("http://localhost") 105 | .called(); 106 | httpClientMock.verify() 107 | .get("http://www.google.com") 108 | .called(2); 109 | httpClientMock.verify() 110 | .get("http://example.com") 111 | .called(3); 112 | } 113 | 114 | @Test 115 | public void shouldVerifyBodyContent() throws IOException { 116 | HttpClientMock httpClientMock = new HttpClientMock(); 117 | 118 | httpClientMock.execute(httpPost("http://localhost", "foo")); 119 | httpClientMock.execute(httpPost("http://localhost", "foo")); 120 | 121 | httpClientMock.execute(httpPut("http://localhost", "bar")); 122 | httpClientMock.execute(httpPut("http://localhost", "foo")); 123 | 124 | httpClientMock.verify() 125 | .post("http://localhost") 126 | .withBody(containsString("foo")) 127 | .called(2); 128 | httpClientMock.verify() 129 | .put("http://localhost") 130 | .withBody(containsString("bar")) 131 | .called(); 132 | httpClientMock.verify() 133 | .get("http://localhost") 134 | .withBody(containsString("foo bar")) 135 | .notCalled(); 136 | } 137 | 138 | @Test 139 | public void should_handle_path_with_query_parameter() throws IOException { 140 | HttpClientMock httpClientMock = new HttpClientMock(); 141 | 142 | httpClientMock.execute(httpPost("http://localhost?a=1&b=2&c=3")); 143 | httpClientMock.execute(httpPost("http://localhost?a=1&b=2")); 144 | httpClientMock.execute(httpPost("http://localhost?a=1")); 145 | 146 | httpClientMock.verify() 147 | .post("http://localhost?d=3") 148 | .notCalled(); 149 | httpClientMock.verify() 150 | .post("http://localhost?a=3") 151 | .notCalled(); 152 | httpClientMock.verify() 153 | .post("http://localhost?a=1&b=2&c=3") 154 | .called(1); 155 | httpClientMock.verify() 156 | .post("http://localhost?a=1&b=2") 157 | .called(2); 158 | httpClientMock.verify() 159 | .post("http://localhost?a=1") 160 | .called(3); 161 | httpClientMock.verify() 162 | .post("http://localhost") 163 | .withParameter("a", "1") 164 | .called(3); 165 | 166 | httpClientMock.verify() 167 | .post("http://localhost") 168 | .withExtraParameters() 169 | .called(3); 170 | httpClientMock.verify() 171 | .post("http://localhost") 172 | .withoutExtraParameters() 173 | .notCalled(); 174 | } 175 | 176 | @Test 177 | public void should_handle_path_with_reference() throws IOException { 178 | HttpClientMock httpClientMock = new HttpClientMock(); 179 | 180 | httpClientMock.execute(httpPost("http://localhost?a=1#abc")); 181 | httpClientMock.execute(httpPost("http://localhost#xyz")); 182 | 183 | httpClientMock.verify() 184 | .post("http://localhost?a=1#abc") 185 | .called(1); 186 | httpClientMock.verify() 187 | .post("http://localhost#abc") 188 | .withoutExtraParameters() 189 | .notCalled(); 190 | httpClientMock.verify() 191 | .post("http://localhost#xyz") 192 | .called(1); 193 | httpClientMock.verify() 194 | .post("http://localhost") 195 | .notCalled(); 196 | } 197 | 198 | @Test 199 | public void should_throw_exception_when_number_of_calls_is_wrong() throws IOException { 200 | HttpClientMock httpClientMock = new HttpClientMock(); 201 | 202 | httpClientMock.execute(httpPost("http://localhost?a=1")); 203 | 204 | Assertions.assertThrows( 205 | Exception.class, 206 | () -> httpClientMock.verify().post("http://localhost?a=1#abc").called(2) 207 | ); 208 | } 209 | 210 | @Test 211 | public void should_allow_different_host_then_default() throws IOException { 212 | HttpClientMock httpClientMock = new HttpClientMock("http://localhost"); 213 | 214 | httpClientMock.onGet("/login").doReturn("login"); 215 | httpClientMock.onGet("http://www.google.com").doReturn("google"); 216 | 217 | httpClientMock.execute(new HttpGet("http://localhost/login")); 218 | httpClientMock.execute(new HttpGet("http://www.google.com")); 219 | 220 | httpClientMock.verify().get("/login").called(); 221 | httpClientMock.verify().get("http://www.google.com").called(); 222 | } 223 | 224 | @Test 225 | public void should_check_header() throws IOException { 226 | HttpClientMock httpClientMock = new HttpClientMock("http://localhost:8080"); 227 | 228 | httpClientMock.onGet("/login").doReturn("OK"); 229 | 230 | HttpGet getMozilla = new HttpGet("http://localhost:8080/login"); 231 | HttpGet getChrome = new HttpGet("http://localhost:8080/login"); 232 | getMozilla.addHeader("User-Agent", "Mozilla"); 233 | getChrome.addHeader("User-Agent", "Chrome"); 234 | httpClientMock.execute(getChrome); 235 | httpClientMock.execute(getMozilla); 236 | 237 | httpClientMock.verify().get("/login").withHeader("User-Agent", "Mozilla").called(); 238 | httpClientMock.verify().get("/login").withHeader("User-Agent", "Chrome").called(); 239 | httpClientMock.verify().get("/login").withHeader("User-Agent", "IE").notCalled(); 240 | } 241 | 242 | @Test 243 | public void withFormParameter_should_match_when_allParametersHaveMatchingValue() throws IOException { 244 | HttpClientMock httpClientMock = new HttpClientMock("http://localhost"); 245 | 246 | HttpPost request = new HttpPost("http://localhost/login"); 247 | request.setEntity(new UrlEncodedFormEntity(Arrays.asList( 248 | new BasicNameValuePair("username", "John"), 249 | new BasicNameValuePair("password", "secret!") 250 | ))); 251 | httpClientMock.execute(request); 252 | 253 | httpClientMock.verify() 254 | .post("/login") 255 | .withFormParameter("username", "John") 256 | .withFormParameter("password", Matchers.containsString("secret")) 257 | .called(); 258 | } 259 | 260 | @Test 261 | public void withFormParameter_should_notMatch_when_parameterHaveNotMatchingValue() throws IOException { 262 | HttpClientMock httpClientMock = new HttpClientMock("http://localhost"); 263 | 264 | HttpPost request = new HttpPost("http://localhost/login"); 265 | request.setEntity(new UrlEncodedFormEntity(Arrays.asList( 266 | new BasicNameValuePair("username", "John"), 267 | new BasicNameValuePair("password", "secret!") 268 | ))); 269 | httpClientMock.execute(request); 270 | 271 | httpClientMock.verify() 272 | .post("/login") 273 | .withFormParameter("username", "John") 274 | .withFormParameter("password", Matchers.containsString("abc")) 275 | .notCalled(); 276 | } 277 | 278 | @Test 279 | public void withFormParameters_should_match_when_allParametersHaveMatchingValue() throws IOException { 280 | HttpClientMock httpClientMock = new HttpClientMock("http://localhost"); 281 | 282 | HttpPost request = new HttpPost("http://localhost/login"); 283 | request.setEntity(new UrlEncodedFormEntity(Arrays.asList( 284 | new BasicNameValuePair("username", "John"), 285 | new BasicNameValuePair("password", "secret!") 286 | ))); 287 | httpClientMock.execute(request); 288 | 289 | ParametersMatcher parameters = new ParametersMatcher(); 290 | parameters.put("username", Matchers.equalTo("John")); 291 | parameters.put("password", Matchers.containsString("secret")); 292 | httpClientMock.verify().post("/login") 293 | .withFormParameters(parameters) 294 | .called(); 295 | } 296 | 297 | @Test 298 | public void withFormParameters_should_match_when_requestHasExtraParametersAndAllowExtraParametersIsTrue() throws IOException { 299 | HttpClientMock httpClientMock = new HttpClientMock("http://localhost"); 300 | 301 | HttpPost request = new HttpPost("http://localhost/login"); 302 | request.setEntity(new UrlEncodedFormEntity(Arrays.asList( 303 | new BasicNameValuePair("username", "John"), 304 | new BasicNameValuePair("password", "secret!") 305 | ))); 306 | httpClientMock.execute(request); 307 | 308 | ParametersMatcher parameters = new ParametersMatcher(); 309 | parameters.put("username", Matchers.equalTo("John")); 310 | 311 | httpClientMock.verify().post("/login") 312 | .withFormParameters(parameters) 313 | .withExtraFormParameters() 314 | .called(); 315 | } 316 | 317 | @Test 318 | public void withFormParameters_should_notMatch_when_requestHasExtraParametersAndAllowExtraFormParametersIsFalse() throws IOException { 319 | HttpClientMock httpClientMock = new HttpClientMock("http://localhost"); 320 | 321 | HttpPost request = new HttpPost("http://localhost/login"); 322 | request.setEntity(new UrlEncodedFormEntity(Arrays.asList( 323 | new BasicNameValuePair("username", "John"), 324 | new BasicNameValuePair("password", "secret!") 325 | ))); 326 | httpClientMock.execute(request); 327 | 328 | ParametersMatcher parameters = new ParametersMatcher(); 329 | parameters.put("username", Matchers.equalTo("John")); 330 | 331 | httpClientMock.verify().post().withPath("/login") 332 | .withFormParameters(parameters) 333 | .withoutExtraFormParameters() 334 | .notCalled(); 335 | } 336 | 337 | @Test 338 | public void withFormParameters_when_requestHasExtraFormParameters() throws IOException { 339 | HttpClientMock httpClientMock = new HttpClientMock("http://localhost"); 340 | 341 | HttpPost request = new HttpPost("http://localhost/login"); 342 | request.setEntity(new UrlEncodedFormEntity(Arrays.asList( 343 | new BasicNameValuePair("username", "John"), 344 | new BasicNameValuePair("password", "secret!") 345 | ))); 346 | httpClientMock.execute(request); 347 | 348 | ParametersMatcher parameters = new ParametersMatcher(); 349 | parameters.put("username", Matchers.equalTo("John")); 350 | 351 | httpClientMock.verify().post("/login") 352 | .withFormParameters(parameters) 353 | .withoutExtraFormParameters() 354 | .notCalled(); 355 | httpClientMock.verify().post("/login") 356 | .withFormParameters(parameters) 357 | .withExtraFormParameters() 358 | .called(); 359 | } 360 | 361 | } 362 | -------------------------------------------------------------------------------- /src/test/java/com/github/paweladamski/httpclientmock/Requests.java: -------------------------------------------------------------------------------- 1 | package com.github.paweladamski.httpclientmock; 2 | 3 | import java.io.UnsupportedEncodingException; 4 | import org.apache.http.HttpEntity; 5 | import org.apache.http.client.methods.HttpGet; 6 | import org.apache.http.client.methods.HttpPost; 7 | import org.apache.http.client.methods.HttpPut; 8 | import org.apache.http.client.methods.HttpUriRequest; 9 | import org.apache.http.entity.StringEntity; 10 | 11 | class Requests { 12 | 13 | public static HttpUriRequest httpGet(String host) { 14 | return new HttpGet(host); 15 | } 16 | 17 | public static HttpUriRequest httpPost(String host) throws UnsupportedEncodingException { 18 | return httpPost(host, ""); 19 | } 20 | 21 | public static HttpUriRequest httpPost(String host, String content) throws UnsupportedEncodingException { 22 | HttpPost post = new HttpPost(host); 23 | HttpEntity entity = new StringEntity(content); 24 | post.setEntity(entity); 25 | return post; 26 | } 27 | 28 | public static HttpUriRequest httpPut(String host, String content) throws UnsupportedEncodingException { 29 | HttpPut post = new HttpPut(host); 30 | HttpEntity entity = new StringEntity(content); 31 | post.setEntity(entity); 32 | return post; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/test/java/com/github/paweladamski/httpclientmock/ThrowingRunnable.java: -------------------------------------------------------------------------------- 1 | package com.github.paweladamski.httpclientmock; 2 | 3 | @FunctionalInterface 4 | interface ThrowingRunnable { 5 | 6 | void run() throws Exception; 7 | } 8 | -------------------------------------------------------------------------------- /src/test/java/com/github/paweladamski/httpclientmock/UrlParamsParserTest.java: -------------------------------------------------------------------------------- 1 | package com.github.paweladamski.httpclientmock; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | 5 | import java.util.List; 6 | import org.apache.http.NameValuePair; 7 | import org.junit.jupiter.api.Test; 8 | 9 | public class UrlParamsParserTest { 10 | 11 | @Test 12 | public void shouldParseQueryString() { 13 | List params = new UrlParamsParser().parse("a=1&b=2"); 14 | assertEquals("a", params.get(0).getName()); 15 | assertEquals("1", params.get(0).getValue()); 16 | assertEquals("b", params.get(1).getName()); 17 | assertEquals("2", params.get(1).getValue()); 18 | } 19 | 20 | @Test 21 | public void shouldReturnEmptyListForNull() { 22 | assertEquals(0, new UrlParamsParser().parse(null).size()); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/test/java/com/github/paweladamski/httpclientmock/UrlParserTest.java: -------------------------------------------------------------------------------- 1 | package com.github.paweladamski.httpclientmock; 2 | 3 | import static com.github.paweladamski.httpclientmock.UrlParser.EMPTY_PORT_NUMBER; 4 | import static org.junit.jupiter.api.Assertions.assertTrue; 5 | 6 | import org.junit.jupiter.api.BeforeEach; 7 | import org.junit.jupiter.api.Test; 8 | 9 | public class UrlParserTest { 10 | 11 | private UrlParser urlParser; 12 | 13 | @BeforeEach 14 | public void setUp() { 15 | urlParser = new UrlParser(); 16 | } 17 | 18 | @Test 19 | public void parseHost() { 20 | UrlConditions urlConditions = urlParser.parse("http://localhost"); 21 | assertTrue(urlConditions.getHostConditions().get(0).matches("localhost")); 22 | assertTrue(urlConditions.getPortConditions().get(0).matches(EMPTY_PORT_NUMBER)); 23 | assertTrue(urlConditions.getReferenceConditions().matches("")); 24 | } 25 | 26 | @Test 27 | public void parseHostWithPort() { 28 | UrlConditions urlConditions = urlParser.parse("http://localhost:8080"); 29 | assertTrue(urlConditions.getHostConditions().get(0).matches("localhost")); 30 | assertTrue(urlConditions.getPortConditions().get(0).matches(8080)); 31 | } 32 | 33 | @Test 34 | public void parseHostAndPath() { 35 | UrlConditions urlConditions = urlParser.parse("http://localhost/foo/bar"); 36 | assertTrue(urlConditions.getHostConditions().get(0).matches("localhost")); 37 | assertTrue(urlConditions.getPathConditions().get(0).matches("/foo/bar")); 38 | } 39 | 40 | @Test 41 | public void parseHostAndPathAndParameters() { 42 | UrlConditions urlConditions = urlParser.parse("http://localhost/foo/bar?a=1&b=2"); 43 | assertTrue(urlConditions.getHostConditions().get(0).matches("localhost")); 44 | assertTrue(urlConditions.getPathConditions().get(0).matches("/foo/bar")); 45 | assertTrue(urlConditions.getUrlQueryConditions().matches("a=1&b=2")); 46 | } 47 | 48 | @Test 49 | public void parseHostReference() { 50 | UrlConditions urlConditions = urlParser.parse("http://localhost#abc"); 51 | assertTrue(urlConditions.getHostConditions().get(0).matches("localhost")); 52 | assertTrue(urlConditions.getReferenceConditions().matches("abc")); 53 | } 54 | 55 | } 56 | 57 | 58 | -------------------------------------------------------------------------------- /src/test/java/com/github/paweladamski/httpclientmock/action/UrlEncodedFormEntityResponseTest.java: -------------------------------------------------------------------------------- 1 | package com.github.paweladamski.httpclientmock.action; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | 5 | import java.nio.charset.StandardCharsets; 6 | import java.util.Arrays; 7 | import java.util.List; 8 | import org.apache.http.HttpResponse; 9 | import org.apache.http.NameValuePair; 10 | import org.apache.http.client.utils.URLEncodedUtils; 11 | import org.apache.http.message.BasicNameValuePair; 12 | import org.junit.jupiter.api.Test; 13 | 14 | /** 15 | * @author Michael Angstadt 16 | */ 17 | public class UrlEncodedFormEntityResponseTest { 18 | 19 | @Test 20 | public void test() throws Exception { 21 | List expectedPairs = Arrays.asList(new BasicNameValuePair("one", "1"), new BasicNameValuePair("two", "2")); 22 | int expectedStatus = 500; 23 | 24 | UrlEncodedFormEntityResponse action = new UrlEncodedFormEntityResponse(expectedStatus, expectedPairs, StandardCharsets.UTF_8); 25 | HttpResponse response = action.getResponse(null); 26 | 27 | List actualPairs = URLEncodedUtils.parse(response.getEntity()); 28 | assertEquals(expectedPairs, actualPairs); 29 | 30 | assertEquals(expectedStatus, response.getStatusLine().getStatusCode()); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/test/java/com/github/paweladamski/httpclientmock/condition/UrlEncodedFormConditionTest.java: -------------------------------------------------------------------------------- 1 | package com.github.paweladamski.httpclientmock.condition; 2 | 3 | import static org.hamcrest.MatcherAssert.assertThat; 4 | import static org.hamcrest.Matchers.contains; 5 | import static org.hamcrest.Matchers.empty; 6 | import static org.hamcrest.Matchers.equalTo; 7 | import static org.junit.jupiter.api.Assertions.assertFalse; 8 | import static org.junit.jupiter.api.Assertions.assertTrue; 9 | 10 | import com.github.paweladamski.httpclientmock.Debugger; 11 | import com.github.paweladamski.httpclientmock.Request; 12 | import java.util.ArrayList; 13 | import java.util.Arrays; 14 | import java.util.List; 15 | import org.apache.http.HttpEntity; 16 | import org.apache.http.client.entity.UrlEncodedFormEntity; 17 | import org.apache.http.client.methods.HttpGet; 18 | import org.apache.http.client.methods.HttpPost; 19 | import org.apache.http.client.utils.URLEncodedUtils; 20 | import org.apache.http.entity.StringEntity; 21 | import org.apache.http.message.BasicNameValuePair; 22 | import org.junit.jupiter.api.Test; 23 | 24 | /** 25 | * @author Michael Angstadt 26 | */ 27 | public class UrlEncodedFormConditionTest { 28 | 29 | @Test 30 | public void valid_match() throws Exception { 31 | UrlEncodedFormCondition condition = new UrlEncodedFormCondition(); 32 | condition.addExpectedParameter("one", equalTo("1")); 33 | condition.addExpectedParameter("two", equalTo("2")); 34 | 35 | HttpPost request = new HttpPost(); 36 | request.setEntity(new UrlEncodedFormEntity(Arrays.asList( 37 | new BasicNameValuePair("one", "1"), 38 | new BasicNameValuePair("two", "2") 39 | ))); 40 | 41 | Request r = new Request(null, request, null); 42 | assertTrue(condition.matches(r)); 43 | 44 | TestDebugger debugger = new TestDebugger(); 45 | condition.debug(r, debugger); 46 | assertThat(debugger.matching, contains( 47 | "form parameter one is \"1\"", 48 | "form parameter two is \"2\"" 49 | )); 50 | assertThat(debugger.notMatching, empty()); 51 | } 52 | 53 | @Test 54 | public void case_sensitive_names() throws Exception { 55 | UrlEncodedFormCondition condition = new UrlEncodedFormCondition(); 56 | condition.addExpectedParameter("ONE", equalTo("1")); 57 | 58 | HttpPost request = new HttpPost(); 59 | request.setEntity(new UrlEncodedFormEntity(Arrays.asList( 60 | new BasicNameValuePair("one", "1") 61 | ))); 62 | 63 | Request r = new Request(null, request, null); 64 | assertFalse(condition.matches(r)); 65 | 66 | TestDebugger debugger = new TestDebugger(); 67 | condition.debug(r, debugger); 68 | assertThat(debugger.matching, empty()); 69 | assertThat(debugger.notMatching, contains( 70 | "form parameter ONE is missing from the request", 71 | "form parameter one was not expected to be in the request" 72 | )); 73 | } 74 | 75 | @Test 76 | public void no_match() throws Exception { 77 | UrlEncodedFormCondition condition = new UrlEncodedFormCondition(); 78 | condition.addExpectedParameter("one", equalTo("1")); 79 | condition.addExpectedParameter("two", equalTo("2")); 80 | 81 | HttpPost request = new HttpPost(); 82 | request.setEntity(new UrlEncodedFormEntity(Arrays.asList( 83 | new BasicNameValuePair("one", "1"), 84 | new BasicNameValuePair("two", "not 2") 85 | ))); 86 | 87 | Request r = new Request(null, request, null); 88 | assertFalse(condition.matches(r)); 89 | 90 | TestDebugger debugger = new TestDebugger(); 91 | condition.debug(r, debugger); 92 | assertThat(debugger.matching, contains( 93 | "form parameter one is \"1\"" 94 | )); 95 | assertThat(debugger.notMatching, contains( 96 | "form parameter two is \"2\"" 97 | )); 98 | } 99 | 100 | /** 101 | * Parameters with the same name are not supported because there's no way of telling which Matcher to assign to which parameter. 102 | */ 103 | @Test 104 | public void duplicate_names() throws Exception { 105 | UrlEncodedFormCondition condition = new UrlEncodedFormCondition(); 106 | condition.addExpectedParameter("one", equalTo("1")); 107 | condition.addExpectedParameter("one", equalTo( 108 | "3")); //MatchersMap requires that the parameter value match BOTH conditions--so the value must equal "1" and must also equal "3", which is impossible 109 | 110 | HttpPost request = new HttpPost(); 111 | request.setEntity(new UrlEncodedFormEntity(Arrays.asList( 112 | new BasicNameValuePair("one", "1"), 113 | new BasicNameValuePair("one", "3") 114 | ))); 115 | 116 | Request r = new Request(null, request, null); 117 | assertFalse(condition.matches(r)); 118 | 119 | TestDebugger debugger = new TestDebugger(); 120 | condition.debug(r, debugger); 121 | assertThat(debugger.matching, empty()); 122 | assertThat(debugger.notMatching, contains( 123 | "form parameter one is \"1\" and \"3\"", 124 | "form parameter one is \"1\" and \"3\"" 125 | )); 126 | } 127 | 128 | @Test 129 | public void different_number_of_parameters() throws Exception { 130 | UrlEncodedFormCondition condition = new UrlEncodedFormCondition(); 131 | condition.addExpectedParameter("one", equalTo("1")); 132 | 133 | HttpPost request = new HttpPost(); 134 | request.setEntity(new UrlEncodedFormEntity(Arrays.asList( 135 | new BasicNameValuePair("one", "1"), 136 | new BasicNameValuePair("two", "2") 137 | ))); 138 | 139 | Request r = new Request(null, request, null); 140 | assertFalse(condition.matches(r)); 141 | 142 | TestDebugger debugger = new TestDebugger(); 143 | condition.debug(r, debugger); 144 | assertThat(debugger.matching, contains( 145 | "form parameter one is \"1\"" 146 | )); 147 | assertThat(debugger.notMatching, contains( 148 | "form parameter two was not expected to be in the request" 149 | )); 150 | } 151 | 152 | /** 153 | * The method that is used to extract the form parameters out of the request body ({@link URLEncodedUtils#parse(HttpEntity)}) also takes the Content-Type of 154 | * the request into consideration. If the Content-Type is not "application/x-www-form-urlencoded", then it will not attempt to parse the body and it will act 155 | * as if the body has zero form parameters in it. 156 | */ 157 | @Test 158 | public void body_contains_form_parameters_but_content_type_is_different() throws Exception { 159 | { 160 | UrlEncodedFormCondition condition = new UrlEncodedFormCondition(); 161 | 162 | HttpPost request = new HttpPost(); 163 | request.setEntity(new StringEntity("one=1")); 164 | 165 | Request r = new Request(null, request, null); 166 | assertTrue(condition.matches(r)); 167 | 168 | TestDebugger debugger = new TestDebugger(); 169 | condition.debug(r, debugger); 170 | assertThat(debugger.matching, empty()); 171 | assertThat(debugger.notMatching, empty()); 172 | } 173 | 174 | { 175 | UrlEncodedFormCondition condition = new UrlEncodedFormCondition(); 176 | condition.addExpectedParameter("one", equalTo("1")); 177 | 178 | HttpPost request = new HttpPost(); 179 | request.setEntity(new StringEntity("one=1")); 180 | 181 | Request r = new Request(null, request, null); 182 | assertFalse(condition.matches(r)); //request does not use "application/x-www-form-urlencoded" content type 183 | 184 | TestDebugger debugger = new TestDebugger(); 185 | condition.debug(r, debugger); 186 | assertThat(debugger.matching, empty()); 187 | assertThat(debugger.notMatching, contains( 188 | "form parameter one is missing from the request" 189 | )); 190 | } 191 | } 192 | 193 | @Test 194 | public void bodyless_request() { 195 | //without expected params 196 | { 197 | UrlEncodedFormCondition condition = new UrlEncodedFormCondition(); 198 | HttpGet request = new HttpGet(); 199 | Request r = new Request(null, request, null); 200 | assertTrue(condition.matches(r)); 201 | 202 | TestDebugger debugger = new TestDebugger(); 203 | condition.debug(r, debugger); 204 | assertThat(debugger.matching, empty()); 205 | assertThat(debugger.notMatching, empty()); 206 | } 207 | 208 | //with expected params 209 | { 210 | UrlEncodedFormCondition condition = new UrlEncodedFormCondition(); 211 | condition.addExpectedParameter("foo", equalTo("bar")); 212 | HttpGet request = new HttpGet(); 213 | Request r = new Request(null, request, null); 214 | assertFalse(condition.matches(r)); 215 | 216 | TestDebugger debugger = new TestDebugger(); 217 | condition.debug(r, debugger); 218 | assertThat(debugger.matching, empty()); 219 | assertThat(debugger.notMatching, contains( 220 | "form parameter foo is missing from the request" 221 | )); 222 | } 223 | } 224 | 225 | private static class TestDebugger extends Debugger { 226 | 227 | public final List matching = new ArrayList<>(); 228 | public final List notMatching = new ArrayList<>(); 229 | 230 | @Override 231 | public void message(boolean matches, String message) { 232 | List list = matches ? matching : notMatching; 233 | list.add(message); 234 | } 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /src/test/java/com/github/paweladamski/httpclientmock/matchers/ParametersMatcherTest.java: -------------------------------------------------------------------------------- 1 | package com.github.paweladamski.httpclientmock.matchers; 2 | 3 | import static org.hamcrest.Matchers.equalTo; 4 | import static org.junit.jupiter.api.Assertions.assertFalse; 5 | import static org.junit.jupiter.api.Assertions.assertTrue; 6 | 7 | import java.util.ArrayList; 8 | import java.util.Collections; 9 | import org.apache.http.NameValuePair; 10 | import org.apache.http.message.BasicNameValuePair; 11 | import org.hamcrest.Matchers; 12 | import org.junit.jupiter.api.BeforeEach; 13 | import org.junit.jupiter.api.Test; 14 | 15 | public class ParametersMatcherTest { 16 | 17 | ParametersMatcher matcher; 18 | ArrayList actualParameters; 19 | 20 | @BeforeEach 21 | public void setUp() { 22 | matcher = new ParametersMatcher(); 23 | actualParameters = new ArrayList<>(); 24 | } 25 | 26 | @Test 27 | public void match_shouldReturnTrue_When_NoParamsWereExpectedAndNoParamsInActualList() { 28 | assertTrue(matcher.matches(Collections.EMPTY_LIST)); 29 | } 30 | 31 | @Test 32 | public void matchesAndAllowExtraParameters_shouldReturnTrue_When_NoParamsWereExpectedAndNoParamsInActualList() { 33 | assertTrue(matcher.matchesAndAllowExtraParameters(Collections.EMPTY_LIST)); 34 | } 35 | 36 | @Test 37 | public void match_shouldReturnTrue_When_OneParamIsExpectedAndTheSameParamsInActualList() { 38 | matcher.put("foo", equalTo("bar")); 39 | actualParameters.add(new BasicNameValuePair("foo", "bar")); 40 | 41 | assertTrue(matcher.matches(actualParameters)); 42 | } 43 | 44 | @Test 45 | public void matchesAndAllowExtraParameters_shouldReturnTrue_When_OneParamIsExpectedAndTheSameParamsInActualList() { 46 | matcher.put("foo", equalTo("bar")); 47 | actualParameters.add(new BasicNameValuePair("foo", "bar")); 48 | 49 | assertTrue(matcher.matches(actualParameters)); 50 | } 51 | 52 | @Test 53 | public void match_shouldReturnFalse_When_ActualParametersHaveOneExtraParameter() { 54 | matcher.put("foo", equalTo("bar")); 55 | actualParameters.add(new BasicNameValuePair("foo", "bar")); 56 | actualParameters.add(new BasicNameValuePair("abc", "123")); 57 | 58 | assertFalse(matcher.matches(actualParameters)); 59 | } 60 | 61 | @Test 62 | public void matchesAndAllowExtraParameters_shouldReturnTrue_When_ActualParametersHaveOneExtraParameter() { 63 | matcher.put("foo", equalTo("bar")); 64 | actualParameters.add(new BasicNameValuePair("foo", "bar")); 65 | actualParameters.add(new BasicNameValuePair("abc", "123")); 66 | 67 | assertTrue(matcher.matchesAndAllowExtraParameters(actualParameters)); 68 | } 69 | 70 | @Test 71 | public void match_shouldReturnFalse_When_ActualContainNotMatchingParameter() { 72 | matcher.put("foo", equalTo("bar")); 73 | actualParameters.add(new BasicNameValuePair("foo", "123")); 74 | 75 | assertFalse(matcher.matches(actualParameters)); 76 | } 77 | 78 | @Test 79 | public void matchesAndAllowExtraParameters_shouldReturnFalse_When_ActualContainNotMatchingParameter() { 80 | matcher.put("foo", equalTo("bar")); 81 | actualParameters.add(new BasicNameValuePair("foo", "123")); 82 | 83 | assertFalse(matcher.matchesAndAllowExtraParameters(actualParameters)); 84 | } 85 | 86 | @Test 87 | public void match_shouldReturnFalse_When_ActualMissesOneOfTheExpectedParameter() { 88 | matcher.put("foo", equalTo("bar")); 89 | matcher.put("abc", equalTo("123")); 90 | actualParameters.add(new BasicNameValuePair("foo", "bar")); 91 | 92 | assertFalse(matcher.matches(actualParameters)); 93 | } 94 | 95 | @Test 96 | public void matchesAndAllowExtraParameters_shouldReturnFalse_When_ActualMissesOneOfTheExpectedParameter() { 97 | matcher.put("foo", equalTo("bar")); 98 | matcher.put("abc", equalTo("123")); 99 | actualParameters.add(new BasicNameValuePair("foo", "bar")); 100 | 101 | assertFalse(matcher.matchesAndAllowExtraParameters(actualParameters)); 102 | } 103 | 104 | @Test 105 | public void put_should_addNewMatchers_when_addingMatcherForExistingParameter() { 106 | matcher.put("foo", Matchers.containsString("a")); 107 | matcher.put("foo", Matchers.containsString("b")); 108 | 109 | actualParameters.add(new BasicNameValuePair("foo", "a")); 110 | assertFalse(matcher.matches(actualParameters)); 111 | 112 | actualParameters.clear(); 113 | actualParameters.add(new BasicNameValuePair("foo", "b")); 114 | assertFalse(matcher.matches(actualParameters)); 115 | 116 | actualParameters.clear(); 117 | actualParameters.add(new BasicNameValuePair("foo", "bar")); 118 | assertTrue(matcher.matches(actualParameters)); 119 | } 120 | 121 | } 122 | --------------------------------------------------------------------------------