Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Advanced HTTP Mocking with WireMock

Advanced HTTP Mocking with WireMock

Video Recording: https://youtu.be/x3MvZ8DFrpE

Create HTTP mocks with WireMock’s REST API from any language for deterministic testing and development. Don’t have mocks yet? No worries. You can record live traffic with WireMock that can be played back. Testing apps that rely on APIs can be painful, flaky and complicated because more than likely, the APIs you rely on are out of your control. It’s time to take control back.

During this talk, you’ll learn how to do the following:

* Record and playback network traffic.

* Programmatically setup mocks via the REST API with cURL, Node.js and Java.

* Simulate error scenarios & latency.

* Verify HTTP calls were made the expected number of times and with expected content.

* Simulate stateful behavior using “scenario” mappings (same endpoint, different results).

Bonuses:

* WireMock is FREE and OPEN SOURCE!

* All of WireMock’s features are accessible via its REST (JSON) interface and its Java API.

* There is a WireMockRule for Java JUnit tests.

* WireMock can run as an embedded HTTP server on an Android device. (with a few config tweaks)

Sam Edwards

May 05, 2017
Tweet

More Decks by Sam Edwards

Other Decks in Programming

Transcript

  1. @HandstandSam #DevFestDC Why Mock HTTP? • Speed • Avoid Rate

    Limits • Avoid Modifying State • Edge Cases
  2. @HandstandSam #DevFestDC Why Mock HTTP? • Speed • Avoid Rate

    Limits • Avoid Modifying State • Edge Cases • Error Scenarios
  3. @HandstandSam #DevFestDC Why Mock HTTP? • Speed • Avoid Rate

    Limits • Avoid Modifying State • Edge Cases • Error Scenarios • Deterministic
  4. @HandstandSam #DevFestDC Why Mock HTTP? • Speed • Avoid Rate

    Limits • Avoid Modifying State • Edge Cases • Error Scenarios • Deterministic • Testing and TDD
  5. @HandstandSam #DevFestDC Running WireMock • Download Standalone JAR from Maven

    Central • java -jar wiremock-standalone-2.6.0.jar • http://localhost:8080/__admin/ • --port • --https-port
  6. @HandstandSam #DevFestDC --record-mappings • java -jar wiremock-standalone-2.6.0.jar --proxy-all http://wiremock.org --record-mappings

    • Record all traffic to file-based WireMock mappings. Client (Web/Mobile) http://wiremock.org /__files /mappings
  7. @HandstandSam #DevFestDC Playback • java -jar wiremock-standalone-2.6.0.jar • Playback traffic

    to file-based WireMock mappings. Client (Web/Mobile) /__files /mappings
  8. @HandstandSam #DevFestDC Stubbing via REST API - String Body curl

    --verbose \ -X POST \ --data '{ "request": { "method": "GET", "urlPath": "/hello" }, "response": { "status": 200, "body": "Hello world!", "headers": { "Content-Type": "text/plain" } } }' \ 'http://localhost:8080/__admin/mappings'
  9. @HandstandSam #DevFestDC Stubbing via REST API - JSON Response curl

    --verbose \ -X POST \ --data '{ "request": { "method": "GET", "url": "/json" }, "response": { "status": 200, "jsonBody": { "numbers": [1, 2, 3] }, "headers": { "Content-Type": "application/json" } } }' \ 'http://localhost:8080/__admin/mappings'
  10. @HandstandSam #DevFestDC Stubbing via REST API - Binary Response curl

    --verbose \ -X POST \ --data '{ "request": { "method": "GET", "url": "/binary" }, "response": { "status": 200, "base64Body" : "WUVTIElOREVFRCE=" } }' \ 'http://localhost:8080/__admin/mappings'
  11. @HandstandSam #DevFestDC URL Pattern Stub curl --verbose \ -X POST

    \ --data '{ "request": { "method": "GET", "urlPathPattern": "/.*/profile$" }, "response": { "status": 200, "body": "User profile goes here.", "headers": { "Content-Type": "text/plain" } } }' \ 'http://localhost:8080/__admin/mappings'
  12. @HandstandSam #DevFestDC Query Parameter Stub curl --data '{ "request": {

    "method": "GET", "urlPath": "/query", "queryParameters":{ "q": { "equalTo": "wiremock" } } }, "response": { "status": 200, "body": "<html><body><h3>It looks like you were searching for WireMock :-) Try <a href=\"http://wiremock.org\">http://wiremock.org</a></h3></body></html>" } }' \ 'http://localhost:8080/__admin/mappings'
  13. @HandstandSam #DevFestDC Header Matching Stub curl --verbose \ -X POST

    \ --data '{ "request": { "method": "GET", "urlPath": "/endpoint", "headers" : { "Accept" : { "contains" : "application/json;v=2" } } }, "response": { "status": 200, "jsonBody": { "version": 2 }, "headers": { "Content-Type": "application/json" } } }' \ 'http://localhost:8080/__admin/mappings'
  14. @HandstandSam #DevFestDC Request Body Matching curl --verbose \ -X POST

    \ --data '{ "request": { "method": "POST", "urlPath": "/worldseries", "bodyPatterns" : [ { "contains" : "Orioles" } ] }, "response": { "status": 200, "body": "Orioles win the 2017 World Series!!!" } }' \ 'http://localhost:8080/__admin/mappings'
  15. @HandstandSam #DevFestDC Request Body Matching Requests curl --data '{ "team":

    "Orioles", "score": 10 }' \ 'http://localhost:8080/worldseries'
  16. @HandstandSam #DevFestDC JSON Body Matching Stub curl --verbose \ -X

    POST \ --data '{ "request": { "method": "POST", "urlPath": "/score", "bodyPatterns" : [ { "equalToJson" : "{ \"score\": 10, \"team\": \"Orioles\" }" } ] }, "response": { "status": 200, "body": "Orioles win the 2018 World Series!!!" } }' \ 'http://localhost:8080/__admin/mappings'
  17. @HandstandSam #DevFestDC JSON Body Matching Requests curl --data '{ "team":

    "Orioles", "score": 10 }' \ 'http://localhost:8080/score' curl --data '{ "score": 10, "team": "Orioles" }' \ 'http://localhost:8080/score' curl --data '{ "score": 1, "team": "Red Sox" }' \ 'http://localhost:8080/score'
  18. @HandstandSam #DevFestDC Stubbing via REST API - “Catch All” for

    Unmatched curl --verbose \ -X POST \ --data '{ "priority":10, "request": { "method": "ANY", "urlPattern": ".*" }, "response": { "status": 404, "jsonBody": {"status":"Error","message":"Endpoint not found"}, "headers": { "Content-Type": "application/json" } } }' \ 'http://localhost:8080/__admin/mappings'
  19. @HandstandSam #DevFestDC Obtaining a Stub Mapping ID Response from WireMock:

    { "id" : "4d37cd0d-53dc-46d6-809d-9de83894e167", "request" : { "url" : "/hello", "method" : "GET" }, "response" : { "status" : 200, "body" : "Hello world!", "headers" : { "Content-Type" : "text/plain" } }, "uuid" : "4d37cd0d-53dc-46d6-809d-9de83894e167" }
  20. @HandstandSam #DevFestDC Editing Stubs via REST API curl --verbose \

    -X PUT \ --data '{ "request": { "method": "GET", "url": "/hello" }, "response": { "status": 200, "body": "Hello world!", "headers": { "Content-Type": "text/plain" } } }' \ 'http://localhost:8080/__admin/mappings/4d37cd0d-53dc-46d6-809d-9de83894e167'
  21. @HandstandSam #DevFestDC Deleting Stubs via REST API curl --verbose \

    -X DELETE \ 'http://localhost:8080/__admin/mappings/4d37cd0d-53dc-46d6-809d-9de83894e167'
  22. @HandstandSam #DevFestDC Pre-Defined Fault Types • EMPTY_RESPONSE ◦ Return a

    completely empty response. • MALFORMED_RESPONSE_CHUNK ◦ Send an OK status header, then garbage, then close the connection. • RANDOM_DATA_THEN_CLOSE ◦ Send garbage then close the connection.
  23. @HandstandSam #DevFestDC Simulating Faults curl --verbose \ -X POST \

    --data '{ "request": { "method": "GET", "url": "/fault" }, "response": { "fault": "MALFORMED_RESPONSE_CHUNK" } }' \ 'http://localhost:8080/__admin/mappings'
  24. @HandstandSam #DevFestDC Stub Specific Fixed Delay curl --verbose \ -X

    POST \ --data '{ "request": { "method": "GET", "url": "/delayed" }, "response": { "status": 200, "body": "Better late than never!", "fixedDelayMilliseconds": 2000 } }' \ 'http://localhost:8080/__admin/mappings'
  25. @HandstandSam #DevFestDC Global Random Delay (Can be set specifically as

    well) curl --verbose \ -X POST \ --data '{ "delayDistribution": { "type": "uniform", "lower": 0, "upper": 5000 } }' \ 'http://localhost:8080/__admin/settings'
  26. @HandstandSam #DevFestDC Verifying Network Calls curl --verbose \ -X POST

    \ --data '{ "method": "GET", "url": "/delayed" }' \ 'http://localhost:8080/__admin/requests/count'
  27. @HandstandSam #DevFestDC Full Journal of Network Requests curl --verbose \

    -X GET \ 'http://localhost:8080/__admin/requests'
  28. @HandstandSam #DevFestDC Stateful Behavior with Scenarios Grocery List Scenario GET

    /items “Started” 1. Bread GET /items “One Item” 1. Bread 2. Milk GET /items “Two Items”
  29. @HandstandSam #DevFestDC Grocery List Scenario - No Items (“Started”) curl

    --verbose \ -X POST \ --data '{ "scenarioName": "Grocery List", "requiredScenarioState": "Started", "newScenarioState": "One Item", "request": { "method": "GET", "url": "/items" }, "response": { "status": 200, "jsonBody" : [] } }' \ 'http://localhost:8080/__admin/mappings'
  30. @HandstandSam #DevFestDC Grocery List Scenario - No Items (“One Item”)

    curl --verbose \ -X POST \ --data '{ "scenarioName": "Grocery List", "requiredScenarioState": "One Item", "newScenarioState": "Two Items", "request": { "method": "GET", "url": "/items" }, "response": { "status": 200, "jsonBody" : ["Milk"] } }' \ 'http://localhost:8080/__admin/mappings'
  31. @HandstandSam #DevFestDC Grocery List Scenario - No Items (“One Item”)

    curl --verbose \ -X POST \ --data '{ "scenarioName": "Grocery List", "requiredScenarioState": "Two Items", "newScenarioState": "Started", "request": { "method": "GET", "url": "/items" }, "response": { "status": 200, "jsonBody" : ["Milk", "Bread"] } }' \ 'http://localhost:8080/__admin/mappings'
  32. @HandstandSam #DevFestDC Resetting WireMock Scenarios curl --verbose \ -X POST

    \ 'http://localhost:8080/__admin/scenarios/reset'
  33. @HandstandSam #DevFestDC File-Based Stubbing Great for: • Recording • Understandability

    • Sharing Not the best for: • Advanced usage • Maintainability
  34. @HandstandSam #DevFestDC Programmatic Stubbing Great for: • Automated Tests •

    Complex Mocks • Numerous Mocks Not the best for: • Non-developers • Simple use cases
  35. @HandstandSam #DevFestDC Language Specific Wrapper Libraries • Java - https://github.com/tomakehurst/wiremock

    • Ruby - https://github.com/cheezy/service_mock • Closure - https://github.com/alexanderjamesking/clj-wiremock • PHP - https://github.com/rowanhill/wiremock-php • Groovy - https://github.com/tomjankes/wiremock-groovy • Node - https://github.com/jlidder/jswiremock
  36. @HandstandSam #DevFestDC WireMock Syntax in Java { "request": { "method":

    "GET", "urlPath": "/categories" }, "response": { "status": 200, "body": "[]" } } stubFor(get(urlPathEqualTo("/categories")).willReturn(aResponse().withStatus(200).withBody("[]")));
  37. @HandstandSam #DevFestDC WireMock 2.x on Android - Making it Work

    Hurdles: • Java 7 Compatibility (Jetty 9.2.x) • Apache HTTP Client
  38. @HandstandSam #DevFestDC WireMock on Android - Dependencies dependencies { compile("com.github.tomakehurst:wiremock:2.6.0")

    { exclude group: 'org.apache.httpcomponents', module: 'httpclient' exclude group: 'org.json', module: 'json' exclude group: 'org.ow2.asm', module: 'asm' } compile 'org.apache.httpcomponents:httpclient-android:4.3.5.1' }
  39. @HandstandSam #DevFestDC WireMock on Android - Project Config Recommended, but

    not required: android { defaultConfig { minSdkVersion 21 multiDexEnabled true } }