Slide 1

Slide 1 text

ADVANCED HTTP MOCKING WITH WIREMOCK Sam Edwards - @HandstandSam

Slide 2

Slide 2 text

@HandstandSam #DevFestDC Why Mock HTTP?

Slide 3

Slide 3 text

@HandstandSam #DevFestDC Why Mock HTTP? ● Speed

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

@HandstandSam #DevFestDC Mock What and Where?

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

@HandstandSam #DevFestDC What is WireMock?

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

@HandstandSam #DevFestDC Running WireMock

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

@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

Slide 22

Slide 22 text

@HandstandSam #DevFestDC Proxying, Recording and Playback

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

@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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

@HandstandSam #DevFestDC Language Agnostic Stubbing via REST API

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

@HandstandSam #DevFestDC Complex Stubbing

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

@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 http://wiremock.org

" } }' \ 'http://localhost:8080/__admin/mappings'

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

@HandstandSam #DevFestDC Simulating Errors and Latency

Slide 51

Slide 51 text

@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.

Slide 52

Slide 52 text

@HandstandSam #DevFestDC Simulating Faults curl --verbose \ -X POST \ --data '{ "request": { "method": "GET", "url": "/fault" }, "response": { "fault": "MALFORMED_RESPONSE_CHUNK" } }' \ 'http://localhost:8080/__admin/mappings'

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

@HandstandSam #DevFestDC Verifying Networking Calls

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

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

Slide 58

Slide 58 text

@HandstandSam #DevFestDC Stateful Behavior with Scenarios

Slide 59

Slide 59 text

@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”

Slide 60

Slide 60 text

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

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

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

Slide 63

Slide 63 text

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

Slide 64

Slide 64 text

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

Slide 65

Slide 65 text

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

Slide 66

Slide 66 text

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

Slide 67

Slide 67 text

@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

Slide 68

Slide 68 text

@HandstandSam #DevFestDC Usage in Java & JUnit

Slide 69

Slide 69 text

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

Slide 70

Slide 70 text

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

Slide 71

Slide 71 text

@HandstandSam #DevFestDC WireMock Syntax in Java { "request": { "method": "GET", "urlPath": "/categories" }, "response": { "status": 200, "body": "[]" } } stubFor(get(urlPathEqualTo("/categories")).willReturn(aResponse().withStatus(200).withBody("[]")));

Slide 72

Slide 72 text

@HandstandSam #DevFestDC WireMock on Android

Slide 73

Slide 73 text

@HandstandSam #DevFestDC

Slide 74

Slide 74 text

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

Slide 75

Slide 75 text

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

Slide 76

Slide 76 text

@HandstandSam #DevFestDC

Slide 77

Slide 77 text

@HandstandSam #DevFestDC

Slide 78

Slide 78 text

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

Slide 79

Slide 79 text

@HandstandSam #DevFestDC BONUS DEMO: Usage From Android

Slide 80

Slide 80 text

@HandstandSam #DevFestDC Thanks!