Sunday, January 20, 2019

Rest API | webservice

Coming sooon.....!

HTTP Services Mocking with Wiremock - Concepts

    Before proceding with the main chapter of mocking services using wiremock and its implementation, lets try to understand What is mock, what is mocking a service, why to mock, where to mock and how to mock and use. Lets start learning the same info step by step. Hope by end of this blog you would be knowing about mocking services, where and how to use.
I tried my best to gather the enough information(atleast required basics) and put in the same for ease of understanding.
Yes. You could get the things done from internet and solve all your problems but my point of writing this is to educate you the same in very less time with all required information atleast the basics. Also it would be mine backup data over google blogspot server for my future reference.

Lets try to understand the below problem definition first:
    We all know that there will be an integration of one service with another(or many) services(internal/external) in a product development organisations. Lets have one scenario where internal service depends on external service as part of running end-to-end flow tests in testing environment.
Below are the factors affecting external service :
a. if server is down for some deployment.
b. cost factor for each service call.
c. delay in providing service(might be due to network connection issue) and so on.

What should be the solution for above scenario?
Mock that external service as simple as you can.

Now what is a service?
Here in my context, service is a http based rest api. For more info "google baba ki jai" or check my blog: Rest API

What is Mocking?
By Oxford dictionaries, mock is "make a replica or imitation of something".
By object-oriented programming, mock objects are simulated objects that mimic the behavior of real objects in controlled ways.

About Wiremock:
WireMock was originally created by Tom Akehurst in 2011.

What is Wiremock?
WireMock is a simulator for HTTP-based APIs and can be used to mock an API(Application Programming Interface) response in case of REST services development and testing.
in other words, wireMock is a tool that can mimic the behavior of an HTTP API. i.e. it can receive the http request from client(web/mobile) that has already been stubbed and send the corresponding canned response by request-matching mechanism.

Why Wiremock?
We can mock the services using various tools like wiremock, mockito, mocklab, soup ui, mackoon etc.
Because of their varied features like speed, robust, userfriendly nature, scalable and lightweight, geeks will choose the best tool. I have choosen the wiremock because for the first time when I tried searching for api mocking tools for office related work requirement, wiremock is the one that has been shown at the top of google search results. I navigated to official website of wiremock home page. Then i thought of it will be good to learn things from scratch for this tool(meanwhile googled for comparison of wiremock with others and found this is being used by many and free version). And thus myself motivated to educate public via this blog way.

How to use wiremock?
i.e. mocking a service using wiremock simulator tool.
Just follow below steps for the solution of this question.
a. Running wiremock:
   -> Download the standalone wiremock jar: http://repo1.maven.org/maven2/com/github/tomakehurst/wiremock-standalone/2.20.0/wiremock-standalone-2.20.0.jar
   -> Deploy the jar on running apache tomcat server which is a daemon process in your local system.
      i.e. in terminal execute this command in path: java -jar wiremock-standalone-<version>.jar
   -> Now the wiremock server would have started running background as daemon process. Don't close the terminal. If you close or interrupt the service, then service will be stopped.
   -> To make ensure whether service is UP(running) or not, in browser(chrome) enter local host url as http://localhost:8080/.
      -->You will see HTTP Status 403 forbidden error which means service is running.
  Note:
  > When the service is run the immediately __files and mappings folders will be created in the path of wiremock jar located in server.
      > If you want to run wiremock service in different port(say 8090) then deploy below service as below:
        java -jar wiremock-standalone-<version>.jar --port 8090
      > If you want to deploy in different port of https service:
        java -jar wiremock-standalone-<version>.jar --https-port 8085
b. Wiremock has the underlying admin api and can be accessed as http://localhost:8080/__admin/
   -> This will shows the empty mappings as below initially as we have not stubbed yet:
      {
  "mappings" : [ ],
  "meta" : {
    "total" : 0
  }
}

c. Proxying and verbose traffic:
   >  java -jar wiremock-standalone-<version>.jar --proxy-all http://wiremock.org
      When you run above service, localhost will redirect you to proxy wiremock webpage. You cant access any other webpages of wiremock. Only homepage will be stubbed(cross check by switching off wifi).
  i.e. enter http://localhost:8080/ in browser. You will see the wiremock webpage.
   >  java -jar wiremock-standalone-<version>.jar --proxy-all http://wiremock.org --verbose
      It behaves same as above but in this case we could see the wiremock traffic(accessed webpage data) of text/images/links/videos in encrypted data(am not sure about the kind of data) in terminal itself where it ran.

d. Record mappings and Playback:
   > java -jar wiremock-standalone-<version>.jar --proxy-all http://wiremock.org --record-mappings
     When above command is executed, service will run and when accessed localhost url, proxy wiremock website will be shown.
a. In __files folder, all the file based images/logos will be recorded.
b. In mappings folder, all the json request-response stub maappings will be recorded.
Note:
> In __files folder, .txt file contains all the html tags. When you delete and write the normal text, the same text will be shown when wiremock started and launched localhost url in browser.

  Above traffic is illustrated below:
  Client <==> WM <==> http://wiremock.org
 
e. Viewing the mappings:
   All the stubbed mappings will be shown under __admin. They are stored here as cache.
   i.e. http://localhost:8080/__admin/ or http://localhost:8080/__admin/mappings
 
f. What is Stubbing?
   stubFor(get(urlEqualTo("/some/thing"))
            .willReturn(aResponse()
                .withHeader("Content-Type", "text/plain")
                .withBody("Hello world!")));
The above code while executing in a test will configure a response with a status of 200 to be returned when the relative URL exactly matches /some/thing (including query parameters). The body of the response will be “Hello world!” and a Content-Type header will be sent with a value of text-plain.
Once above stubbing is executed, we could check the same in /__admin/mappings api. It will be stubbed as below with unique uuid:
{
    "request": {
        "method": "GET",
        "url": "/some/thing"
    },
    "response": {
        "status": 200,
        "body": "Hello world!",
        "headers": {
            "Content-Type": "text/plain"
        }
    }
}

g. Now lets try few simple stubbings:
   Construct the required stubs via curl command and execute in terminal.

1. Stubbing via Rest API - String body

  Lets consider below http service(Rest api) that should be mocked locally:
  a. Requirement part:
  Http method: GET
  Http URL: http://localhost:8080/kkpravee/test1
  Headers: Accept-Type: text/plain
  Response:
      "Hello world!" with header.

  b. Stubbing part:
   curl --verbose \
--data '{
    "request": {
        "method": "GET",
        "urlPath": "/kkpravee/test1"
    },
    "response": {
        "status": 200,
        "body": "Hello world!",
        "headers": {
            "Content-Type": "text/plain"
        }
    }
}' \
'http://localhost:8080/__admin/mappings'

 Execute above curl command in terminal and you should see the below response on successful stub:
 *   Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 8080 (#0)
> POST /__admin/mappings HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.47.0
> Accept: */*
> Content-Length: 240
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 240 out of 240 bytes
< HTTP/1.1 201 Created
< Content-Type: application/json
< Transfer-Encoding: chunked
< Server: Jetty(9.2.z-SNAPSHOT)
<
{
  "id" : "97b0f253-a696-49e5-a1d9-48e77fbbe83f",
  "request" : {
    "urlPath" : "/kkpravee/test1",
    "method" : "GET"
  },
  "response" : {
    "status" : 200,
    "body" : "Hello world!",
    "headers" : {
      "Content-Type" : "text/plain"
    }
  },
  "uuid" : "97b0f253-a696-49e5-a1d9-48e77fbbe83f"
* Connection #0 to host localhost left intact

 c. Verifying in the cache __admin/mappings section for stub mappings entry with unique uuid generation.
    i.e. http://localhost:8080/__admin/mappings
or
check in local path for .json file

 d. Now you could execute the api using curl/postman/restassured client for mocked response as "Hello world!".
    curl 'http://192.168.1.116:8080/kkpravee/test1'

Thus stubbing and cross verification of successful request match stub is done. Similarly we could proceed further for different request-match stubs on required basis.


2. Stubbing via Rest API - JSON Response

  Lets consider below http service(Rest api) that should be mocked locally:
  a. Requirement part:
  Http method: GET
  Http URL: http://localhost:8080/kkpravee/test2
  Headers: Accept-Type: text/plain
  Response:
      {
  "items1": "value1",
  "items2": "value2"
  }

  b. Stubbing part:
   curl --verbose \
--data '{
    "request": {
        "method": "GET",
        "urlPath": "/kkpravee/test2"
    },
    "response": {
        "status": 200,
        "jsonBody": {
  "items1": "value1",
  "items2": "value2"
  },
        "headers": {
            "Content-Type": "application/json"
        }
    }
}' \
'http://localhost:8080/__admin/mappings'

 c. Cross verify whether the stub is success or not by executing below curl command:
    curl http://192.168.1.116:8080/kkpravee/test2
Below is the curl response which is same as expected stubbed response:
{
    "items1": "value1",
    "items2": "value2"
    }
Note:
-> Above host is my local machine IPV4. i.e. same as localhost 127.0.0.1.
-> Also when you observe this stubbed request-response part in __admin cache section, it will be shown in response body json string format(not as per above stub of json).


3. Stubbing via Rest API - Binary Response

  a. Requirement part:
  Http method: GET
  Http URL: http://localhost:8080/kkpravee/test3
  Response:
      Kkpravee binary encoded response mocking

  b. Stubbing part:
   curl --verbose \
--data '{
    "request": {
        "method": "GET",
        "urlPath": "/kkpravee/test3"
    },
    "response": {
        "status": 200,
        "base64Body": "S2twcmF2ZWUgYmluYXJ5IGVuY29kZWQgcmVzcG9uc2UgbW9ja2luZw==",
        "headers": {
            "Content-Type": "application/json"
        }
    }
}' \
'http://localhost:8080/__admin/mappings'

 c. Cross verify whether the stub is success or not by executing below curl command:
    curl http://192.168.1.116:8080/kkpravee/test3
Below is the curl response which is in expected binary decoded format:
-> Kkpravee binary encoded response mocking

h. Now lets get into complex stubbings.
By now we should have a clear picture of stub mocking. From here would be skipping the requirement part and verification parts. Please verify at your end for more understanding.
Http Methods stubbing:
Below are the http methods that on need basis should be stubbed.
ANY
GET
POST
PUT
DELETE
etc....

1. URL pattern stub:

curl --verbose \
--data '{
    "request": {
        "method": "GET",
        "urlPathPattern": "/.*/test4$"
    },
    "response": {
        "status": 200,
        "body": "Kkpravee url pattern test4 response this is!.",
        "headers": {
            "Content-Type": "text/plain"
        }
    }
}' \
'http://localhost:8080/__admin/mappings'

Cross verify by below possible curl pattern URLs:
curl http://192.168.1.116:8080/kkpravee/test4
curl http://192.168.1.116:8080/kkpra/test4
curl http://192.168.1.116:8080/j/test4
Note:
In the urlPathPattern, '$' at the end of url is mandatory for stubbing regex pattern path urls.


2. Query pattern stub:

curl --data '{
    "request": {
        "method": "GET",
        "urlPath": "/kkpravee/test5",
        "queryParameters":{
          "q1": {
            "equalTo": "query param value1"
          }
        }
    },
    "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'

Cross verify the same in browser by using below url and experience the html anchor tag reponse:
http://localhost:8080/kkpravee/test5?q1=query param value1


3. Header matching stub:

curl --data '{
    "request": {
        "method": "GET",
        "urlPath": "/kkpravee/test6",
        "headers":{
          "Accept": {
            "contains": "application/json;v=2"
          }
        }
    },
    "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'

Verify using below command:
curl --header 'Accept: application/json;v=2' \
'http://localhost:8080/kkpravee/test6'


4. Request body matching stub:

a. Requirement part:
Http method: POST
Http uri: http://localhost:8080/kkpravee/test7
Http headers: Accept: application/json;v=2
Request body:
{
  "request1": "value string 1",
  "regAgrss": "requeststringvalue1 agrss",
  "deviceInfo": {
    "geocode": "12.905231,77.7063972",
    "ip": "198.19.49.119",
    "location": "blr",
    "gcmid": "91994988989691994988945678896919949889896919949889896",
    "app": "com.kkpraveetech.upi",
    "os": "android",
    "id": "346c0e096c347354915d95ac013c0ea2",
    "type": "MOB"
  }
}

Response :
{
  "complexStub": "Complex stub is working fine!"
}

b. Stubbing part:
curl --verbose \
--data '{
    "request": {
        "method": "POST",
        "urlPath": "/kkpravee/test7",
"headers":{
          "Accept": {
            "contains": "application/json;v=2"
          }
        },
"bodyPatterns":[
{
     "contains":"requeststringvalue1"
}
]
    },
    "response": {
        "status": 200,
        "jsonBody": {
"complexStub": "Complex stub is working fine!"
},
"headers": {
            "Content-Type": "application/json"
        }
    }
}' \
'http://localhost:8080/__admin/mappings'

c. Verify the same in postman/curl by using above requirement and observe the same response.
Note:
Here we have validated the request response match using "contains" bodyPattern.
bodyPattern exposes few more complex request-response matchings like  "equalToJson" , "ignoreExtraElements", "ignoreArrayOrder" etc.


5. Stubbing via REST API - “Catch All” for Unmatched

  When server is started and we try to access the stub appings that doesnt exist or request-response mismatched then "Request was not matched" message with suggesting Closest stub and requested stubs are shown.
  What if the customer asks to handle it in his customised requirement instead of showing above message?
  Solution: We could stub for his requirement as below:
  curl --verbose \
--data '{
  "priority":10,
  "request": {
    "method": "ANY",
    "urlPattern": ".*"
  },
  "response": {
    "status": 404,
    "jsonBody": {"status":"Error","message":"Rest API endpoint not found"},
    "headers": {
      "Content-Type": "application/json"
    }
  }
}' \
'http://localhost:8080/__admin/mappings'

Note:
 -> Verify above stubbed Error response by construct invalid url path like: http://localhost:8080/fghfvhjkujyiukjh
 -> priority given here is 10. Because it is the least priority given amongst all the stubs.
 -> priorities:1,5,10.
 -> The moment stub is created without any customised priority value in stub, by default it will take as 5.


6. Play around at the created stubs:

 If we want to edit the stub that has already created then we could edit that stub by using its UUID.
 UUID is the unique Id generated for stub creation. By using this UUID we could alter the stub, delete etc.
 UUIDs could be found in __admin/mappings cache page.
 a. Edit the stub:
 curl --verbose \
-X PUT \
--data '{
    "request": {
        "method": "GET",
        "url": "/test9"
    },
    "response": {
        "status": 200,
        "body": "Hello world!",
        "headers": {
            "Content-Type": "text/plain"
        }
    }
}' \
'http://localhost:8080/__admin/mappings/0ab525b3-fae1-4685-aa6a-812e55ea82af'
Note:
-> 4d37cd0d-53dc-46d6-809d-9de83894e167 is the uuid.

 b. Delete the stub:
 curl --verbose \
-X DELETE \
'http://localhost:8080/__admin/mappings/0ab525b3-fae1-4685-aa6a-812e55ea82af'

7. Stubs can be created manually by using wiremock plugin tool added to chrome browser.

8. To reset all the mappings:
curl --verbose \
-X POST \
'http://localhost:8080/__admin/reset'

9. To save all the mappings once server is restarted, then mappings will be vanished. So just save.
curl --verbose \
-X POST \
'http://10.251.32.153:8080/__admin/mappings/save'

10. To stop/shutdown the server via browser:
http://localhost:8080/__admin/shutdown

This is all about the filebased manual effort of request-response match stubbing.

Do you know that we could handle the same process of mocking the services using Programatic too.

     
File based vs Programatic.

File based mocking:
Advantages:
-> Understandability
-> Recording
-> Sharing

Disadvantages:
-> Maintainability
-> Advanced usage

Programatic based mocking:
Advantages:
-> Automation Tests
-> Numerous Mocks
-> Complex Mocks
-> One time stub effort

Disadvantages:
-> Not good for Simple use cases

What kind of stubbing should we use now??


Featured post

Linux commands at work on day to day basis usage

  #To kill process by port: npx kill-port 3000 Or kill -9 $(sudo lsof -t -i:8080) #To run ssh file sh ./filename.sh   #To kill the pid by po...