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)

5701f31a8433a22ae736282de8d08cd6?s=128

Sam Edwards

May 05, 2017
Tweet

Transcript

  1. ADVANCED HTTP MOCKING WITH WIREMOCK Sam Edwards - @HandstandSam

  2. @HandstandSam #DevFestDC Why Mock HTTP?

  3. @HandstandSam #DevFestDC Why Mock HTTP? • Speed

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

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

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

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

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

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

    Limits • Avoid Modifying State • Edge Cases • Error Scenarios • Deterministic • Testing and TDD
  10. @HandstandSam #DevFestDC Mock What and Where?

  11. @HandstandSam #DevFestDC What and Where to Mock? Client (Web/Mobile) HTTP

    Service
  12. @HandstandSam #DevFestDC What and Where to Mock? Client (Web/Mobile)

  13. @HandstandSam #DevFestDC What and Where to Mock? Client (Web/Mobile) HTTP

    Service Backend Services
  14. @HandstandSam #DevFestDC What and Where to Mock? Client (Web/Mobile) HTTP

    Service
  15. @HandstandSam #DevFestDC What and Where to Mock? Client (Web/Mobile) HTTP

    Service Backend Services 3rd Party Services
  16. @HandstandSam #DevFestDC What and Where to Mock? Client (Web/Mobile) HTTP

    Service Backend Services
  17. @HandstandSam #DevFestDC What is WireMock?

  18. @HandstandSam #DevFestDC Open Source, Started in 2011 by Tom Akehurst

  19. @HandstandSam #DevFestDC Running WireMock

  20. @HandstandSam #DevFestDC Open Source Tool for Mocking HTTP Akehurst.

  21. @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
  22. @HandstandSam #DevFestDC Proxying, Recording and Playback

  23. @HandstandSam #DevFestDC --proxy-all • --proxy-all http://wiremock.org Client (Web/Mobile) http://wiremock.org

  24. @HandstandSam #DevFestDC --verbose • See all traffic going through WireMock.

  25. @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
  26. @HandstandSam #DevFestDC Playback • java -jar wiremock-standalone-2.6.0.jar • Playback traffic

    to file-based WireMock mappings. Client (Web/Mobile) /__files /mappings
  27. @HandstandSam #DevFestDC • Before & After • curl http://localhost:8080 Manipulating

    Files Client (Web/Mobile) /__files /mappings
  28. @HandstandSam #DevFestDC • http://localhost:8080/__admin/ Viewing Mappings /__files /mappings

  29. @HandstandSam #DevFestDC Language Agnostic Stubbing via REST API

  30. @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'
  31. @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'
  32. @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'
  33. @HandstandSam #DevFestDC Complex Stubbing

  34. @HandstandSam #DevFestDC HTTP Method Stubbing • ANY • GET •

    POST • PUT • DELETE • etc....
  35. @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'
  36. @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'
  37. @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'
  38. @HandstandSam #DevFestDC Header Matching - Miss curl --header 'Accept: application/json;v=1'

    \ 'http://localhost:8080/endpoint'
  39. @HandstandSam #DevFestDC Header Matching - Match curl --header 'Accept: application/json;v=2'

    \ 'http://localhost:8080/endpoint'
  40. @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'
  41. @HandstandSam #DevFestDC Request Body Matching Requests curl --data '{ "team":

    "Orioles", "score": 10 }' \ 'http://localhost:8080/worldseries'
  42. @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'
  43. @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'
  44. @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'
  45. @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" }
  46. @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'
  47. @HandstandSam #DevFestDC Deleting Stubs via REST API curl --verbose \

    -X DELETE \ 'http://localhost:8080/__admin/mappings/4d37cd0d-53dc-46d6-809d-9de83894e167'
  48. @HandstandSam #DevFestDC https://chrome.google.com/webstore/detail/wiremock-extension/ikiaofdpbmofgmlhajfnhdjelkleljbl

  49. @HandstandSam #DevFestDC Resetting WireMock Server curl --verbose \ -X POST

    \ 'http://localhost:8080/__admin/reset'
  50. @HandstandSam #DevFestDC Simulating Errors and Latency

  51. @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.
  52. @HandstandSam #DevFestDC Simulating Faults curl --verbose \ -X POST \

    --data '{ "request": { "method": "GET", "url": "/fault" }, "response": { "fault": "MALFORMED_RESPONSE_CHUNK" } }' \ 'http://localhost:8080/__admin/mappings'
  53. @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'
  54. @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'
  55. @HandstandSam #DevFestDC Verifying Networking Calls

  56. @HandstandSam #DevFestDC Verifying Network Calls curl --verbose \ -X POST

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

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

  59. @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”
  60. @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'
  61. @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'
  62. @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'
  63. @HandstandSam #DevFestDC Resetting WireMock Scenarios curl --verbose \ -X POST

    \ 'http://localhost:8080/__admin/scenarios/reset'
  64. @HandstandSam #DevFestDC What Kind of Stubbing Should I Use? File

    Based vs. Programmatic
  65. @HandstandSam #DevFestDC File-Based Stubbing Great for: • Recording • Understandability

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

    Complex Mocks • Numerous Mocks Not the best for: • Non-developers • Simple use cases
  67. @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
  68. @HandstandSam #DevFestDC Usage in Java & JUnit

  69. @HandstandSam #DevFestDC Programmatic Usage in Java for a Remote Server

  70. @HandstandSam #DevFestDC Programmatic Usage in Java for a Remote Server

  71. @HandstandSam #DevFestDC WireMock Syntax in Java { "request": { "method":

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

  73. @HandstandSam #DevFestDC

  74. @HandstandSam #DevFestDC WireMock 2.x on Android - Making it Work

    Hurdles: • Java 7 Compatibility (Jetty 9.2.x) • Apache HTTP Client
  75. @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' }
  76. @HandstandSam #DevFestDC

  77. @HandstandSam #DevFestDC

  78. @HandstandSam #DevFestDC WireMock on Android - Project Config Recommended, but

    not required: android { defaultConfig { minSdkVersion 21 multiDexEnabled true } }
  79. @HandstandSam #DevFestDC BONUS DEMO: Usage From Android

  80. @HandstandSam #DevFestDC Thanks!