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. ADVANCED HTTP MOCKING
    WITH WIREMOCK
    Sam Edwards - @HandstandSam

    View Slide

  2. @HandstandSam
    #DevFestDC
    Why Mock HTTP?

    View Slide

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

    View Slide

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

    View Slide

  5. @HandstandSam
    #DevFestDC
    Why Mock HTTP?
    ● Speed
    ● Avoid Rate Limits
    ● Avoid Modifying State

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  10. @HandstandSam
    #DevFestDC
    Mock What and Where?

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  17. @HandstandSam
    #DevFestDC
    What is WireMock?

    View Slide

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

    View Slide

  19. @HandstandSam
    #DevFestDC
    Running WireMock

    View Slide

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

    View Slide

  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

    View Slide

  22. @HandstandSam
    #DevFestDC
    Proxying, Recording and
    Playback

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  26. @HandstandSam
    #DevFestDC
    Playback
    ● java -jar wiremock-standalone-2.6.0.jar
    ● Playback traffic to file-based WireMock mappings.
    Client (Web/Mobile)
    /__files /mappings

    View Slide

  27. @HandstandSam
    #DevFestDC
    ● Before & After
    ● curl http://localhost:8080
    Manipulating Files
    Client (Web/Mobile)
    /__files /mappings

    View Slide

  28. @HandstandSam
    #DevFestDC
    ● http://localhost:8080/__admin/
    Viewing Mappings
    /__files /mappings

    View Slide

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

    View Slide

  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'

    View Slide

  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'

    View Slide

  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'

    View Slide

  33. @HandstandSam
    #DevFestDC
    Complex Stubbing

    View Slide

  34. @HandstandSam
    #DevFestDC
    HTTP Method Stubbing
    ● ANY
    ● GET
    ● POST
    ● PUT
    ● DELETE
    ● etc....

    View Slide

  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'

    View Slide

  36. @HandstandSam
    #DevFestDC
    Query Parameter Stub
    curl --data '{
    "request": {
    "method": "GET",
    "urlPath": "/query",
    "queryParameters":{
    "q": {
    "equalTo": "wiremock"
    }
    }
    },
    "response": {
    "status": 200,
    "body": "It looks like you were searching for WireMock :-) Try href=\"http://wiremock.org\">http://wiremock.org"
    }
    }' \
    'http://localhost:8080/__admin/mappings'

    View Slide

  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'

    View Slide

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

    View Slide

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

    View Slide

  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'

    View Slide

  41. @HandstandSam
    #DevFestDC
    Request Body Matching Requests
    curl --data '{ "team": "Orioles", "score": 10 }' \
    'http://localhost:8080/worldseries'

    View Slide

  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'

    View Slide

  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'

    View Slide

  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'

    View Slide

  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"
    }

    View Slide

  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'

    View Slide

  47. @HandstandSam
    #DevFestDC
    Deleting Stubs via REST API
    curl --verbose \
    -X DELETE \
    'http://localhost:8080/__admin/mappings/4d37cd0d-53dc-46d6-809d-9de83894e167'

    View Slide

  48. @HandstandSam
    #DevFestDC
    https://chrome.google.com/webstore/detail/wiremock-extension/ikiaofdpbmofgmlhajfnhdjelkleljbl

    View Slide

  49. @HandstandSam
    #DevFestDC
    Resetting WireMock Server
    curl --verbose \
    -X POST \
    'http://localhost:8080/__admin/reset'

    View Slide

  50. @HandstandSam
    #DevFestDC
    Simulating Errors and Latency

    View Slide

  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.

    View Slide

  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'

    View Slide

  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'

    View Slide

  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'

    View Slide

  55. @HandstandSam
    #DevFestDC
    Verifying Networking Calls

    View Slide

  56. @HandstandSam
    #DevFestDC
    Verifying Network Calls
    curl --verbose \
    -X POST \
    --data '{
    "method": "GET",
    "url": "/delayed"
    }' \
    'http://localhost:8080/__admin/requests/count'

    View Slide

  57. @HandstandSam
    #DevFestDC
    Full Journal of Network Requests
    curl --verbose \
    -X GET \
    'http://localhost:8080/__admin/requests'

    View Slide

  58. @HandstandSam
    #DevFestDC
    Stateful Behavior with
    Scenarios

    View Slide

  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”

    View Slide

  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'

    View Slide

  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'

    View Slide

  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'

    View Slide

  63. @HandstandSam
    #DevFestDC
    Resetting WireMock Scenarios
    curl --verbose \
    -X POST \
    'http://localhost:8080/__admin/scenarios/reset'

    View Slide

  64. @HandstandSam
    #DevFestDC
    What Kind of Stubbing
    Should I Use?
    File Based vs. Programmatic

    View Slide

  65. @HandstandSam
    #DevFestDC
    File-Based Stubbing
    Great for:
    ● Recording
    ● Understandability
    ● Sharing
    Not the best for:
    ● Advanced usage
    ● Maintainability

    View Slide

  66. @HandstandSam
    #DevFestDC
    Programmatic Stubbing
    Great for:
    ● Automated Tests
    ● Complex Mocks
    ● Numerous Mocks
    Not the best for:
    ● Non-developers
    ● Simple use cases

    View Slide

  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

    View Slide

  68. @HandstandSam
    #DevFestDC
    Usage in Java & JUnit

    View Slide

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

    View Slide

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

    View Slide

  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("[]")));

    View Slide

  72. @HandstandSam
    #DevFestDC
    WireMock on Android

    View Slide

  73. @HandstandSam
    #DevFestDC

    View Slide

  74. @HandstandSam
    #DevFestDC
    WireMock 2.x on Android - Making it Work
    Hurdles:
    ● Java 7 Compatibility (Jetty 9.2.x)
    ● Apache HTTP Client

    View Slide

  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'
    }

    View Slide

  76. @HandstandSam
    #DevFestDC

    View Slide

  77. @HandstandSam
    #DevFestDC

    View Slide

  78. @HandstandSam
    #DevFestDC
    WireMock on Android - Project Config
    Recommended, but not required:
    android {
    defaultConfig {
    minSdkVersion 21
    multiDexEnabled true
    }
    }

    View Slide

  79. @HandstandSam
    #DevFestDC
    BONUS DEMO:
    Usage From Android

    View Slide

  80. @HandstandSam
    #DevFestDC
    Thanks!

    View Slide