From Testing Microservices with Mountebank by Brandon Byars

This article discusses how Mountebank lets you to virtualize services for testing individual microservices.

 


Save 37% on Testing Microservices with Mountebank. Just enter code fccbyars into the discount code box at checkout at manning.com.


 

Once you understand how to integrate with a service, the next step is to figure out how to virtualize it for testing purposes. Let’s virtualize a sample Product Catalog service to test from the Web Façade in isolation:


Figure 1. Virtualizing the Product Catalog service to test the Web Façade


Remember, an imposter is the mountebank term for a virtual service. Mountebank imposters allow you to test individual microservices via virtualization. Mountebank ships with a REST API that lets us create imposters and write tests against them in any language.

Mountebanks, imposters, and funny sounding docs

Much of the mountebank documentation is written in the voice of a true mountebank, prone to hyperbole and false modesty, where even the word “mountebank” shifts from describing the tool itself to the author of the documentation (yours truly). When I originally wrote the tool, I made imposters the core domain concept, in part because it fit the theme of using synonyms for charlatans to describe fake services, and in part because it self-deprecatingly made fun of my own Impostor Syndrome, a chronic ailment of consultants. And yes, as Paul Hammant (one of the original creators of the popular Selenium testing tool and one of the first users of Mountebank) pointed out that impostor (with an “or” instead of “er” at the end) is the “proper” spelling. He also helpfully suggested, now that mountebank is a popular tool used all over the world, complete with a best-selling book, that I change the docs to remove the hipster humor. Unfortunately, he has yet to indicate where I might find the time for such pursuits…

Before we start, we’ll need to install mountebank. Several installation options are listed at www.mbtest.org/docs/install, but we’ll use npm, a package manager that ships with node.js.

 
npm install -g mountebank
 

The -g flag tells npm to install mountebank globally, and you can run it from any directory. Let’s start it up:

 
mb
 

You should see the mountebank log on the terminal:  info:

 
[mb:2525] mountebank v1.9.0 now taking orders -
<linearrow /> point your browser to http://localhost:2525 for help
 

The log will prove invaluable in working with mountebank in the future, and it’s a good idea to familiarize yourself with it. The first word (info, in this case), tells us the log level, which will be either debug, info, warn, or error. The part in brackets (mb:2525) tells us the protocol and port, followed by the actual log message. The administrative port logs as the mb protocol and starts on port 2525 by default (the mb protocol’s HTTP, but mountebank logs it differently to make it easy to spot). The imposters we create use different ports but log to the same output stream in the terminal. The startup log message directs you to open localhost:2525 in your web browser, which provides you the complete set of documentations for your version of mountebank.

To demonstrate creating imposters, we’ll use a utility called curl, which lets you make HTTP calls on the command line. curl comes by default on most Unix-like shells, including Linux and OSX. You can install it on Windows using cygwin, or use PowerShell, which ships with modern versions of Windows (we’ll show a PowerShell example next). Open another terminal window and run the following command:[1]

 
curl – X POST http://localhost:2525/imposters --data ‘{    
  "port": 3000,                                            
  "protocol": "http",                                      
  "stubs": [{                                              
    "responses":[{                                         
      "is": {                                              
        "statusCode": 200,                                 
        "headers: {"Content-Type": "application/json"},    
        "body": {                                          
          "products": [                                    
            {                                              
              "id":"2599b7f4",                             
              "name”: "The Midas Dogbowl",                 
              "Description”: "Pure gold"                                                    
            },                                             
            {                                              
              "id": "e1977c9e",                            
              "name": "Fishtank Amore",                    
              "description": "Show your fish some love"    
            }                                              
          ],                                               
          "_links": {                                      
            "Next: "/products?page=2&itemsPerPage=2"       
          }                                                
        }                                                  
      }                                                    
    }]                                                     
  }]                                                      
}’  
                                                     

A POST to localhost:2525/imposters is how you create new imposters

Each imposter is minimally defined by a port and a protocol

Defines a canned HTTP response based on the HTTP response format we looked at earlier


An important point to note is that we’re passing a JSON object as the body field. As far as HTTP is concerned, a response body is a stream of bytes. Usually that stream is interpreted as a string, which is why mountebank typically expects a string as well. Mountebank supports binary response bodies, encoding them with Base64. That said, most services these days use JSON as their lingua franca. Mountebank, being itself a modern JSON-speaking service, can properly accept a JSON body.The equivalent command on Powershell in Windows expects you to save the request body in a file and pass it to the Invoke-RestMethod command. Save the JSON after the --data parameter from the curl command above into a file called imposter.json, and run the following command from the same directory:

 
Invoke-RestMethod -Method POST -Uri http://localhost:2525/imposters
<linearrow /> -InFile imposter.json
 

Notice what happens in the logs:

 
info: [http:3000] Open for business...
 

The part in brackets now shows the new imposter. As we add more imposters, this becomes increasingly important. All log entries can be disambiguated by looking at the imposter information that prefixes the log message.We can test our imposter on the command line as well, using the curl command we looked at previously:


Figure 2. Using curl to send a request to our virtual Product Catalog service


The curl command prints out the HTTP response as follows:


 
HTTP/1.1 200 OK
Content-Type: application/json
Connection: close
Date: Thu, 19 Jan 2017 14:51:23 GMT
Transfer-Encoding: chunked
 
{
  "products": [
    {
      "id":"2599b7f4",
      "name": "The Midas Dogbowl",
      "Description": "Pure gold"
    },
    {
      "id": "e1977c9e",
      "name”: "Fishtank Amore",
      "description": "Show your fish some love"
    }
  ],
  "_links": {
    "Next: "/products?page=2&itemsPerPage=2"
  }
}
 


A couple of extra headers are in there, and the date has changed, but otherwise the HTTP response is the same as the one returned by the service shown previously. We’re not accounting for all situations. Our imposter returns the same response no matter what the HTTP request looks like. We could fix that by adding predicates to our imposter configuration. As a reminder, a predicate is a set of criteria which the incoming request must match before mountebank sends the associated response. Let’s create an imposter with two products to serve up. It shows an empty result set on the request to the second page of results by using a predicate on the query parameter. For now, restart mb to free up port 3000 by pressing Ctrl-C and typing mb again (we’ll show more elegant ways of cleaning up after ourselves shortly). Then use this command in a separate terminal.


 
Curl – X POST http://localhost:2525/imposters --data ‘{    
   "port": 3000,
   "protocol": "http",
   "stubs": [                                                   
     {
       "predicates": [{
         "equals": {                                            
           "query": { "page": "2" }                             
         }                                                      
       }],
       "responses": [{
         "is": {                                                
           "statusCode": 200,                                   
           "headers": {"Content-Type": "application/json"},     
           "body": { "products": [] }
         }                                                      
       }]
     },
     {
       "responses": [{
         "is": {                                                
           "statusCode": 200,                                   
           "headers": { "Content-Type": "application/json" },   
           "body": {                                            
             "products": [                                      
               {                                                
                 "id": "2599b7f4",                              
                 "name": "The Midas Dogbowl",                   
                 "description": "Pure gold"                     
               },                                               
               {                                                
                 "id": "e1977c9e",                              
                 "name": "Fishtank Amore",                      
                 "description": "Show your fish some love"      
               }                                                
             ],                                                 
             "_links": {                                        
               "next": "/products?page=2&itemsPerPage=2"        
             }                                                  
           }                                                    
         }                                                      
       }]
     }
   ]
},
 

Our imposter now has two stubs, allowing us different responses for different requests

Requires that the request querystring includes page=2

For requests matching the predicate, this response is sent

Because this stub has no predicates, it matches all requests and its response is sent if the request  hasn’t matched predicates in stubs earlier in the array


Exploring the mountebank API on the command line is a great way to get familiar with it and to try out sample imposter configurations. If we change the configuration of our Web Façade to point to localhost:3000 instead of api.petstore.com, we’ll get the products we’ve defined and can manually test the website. We’ve already taken the huge step of decoupling ourselves from the real services.

 

Postman as an alternative to the command line

While using command line tools like curl are great for lightweight experimentation, it’s often useful to have a more graphical approach to organizing different HTTP requests. Postman (www.getpostman.com/) has proven to be an extremely useful tools for playing with HTTP APIs. It started out as a Chrome plugin, but now has downloads for Mac, Windows, and Linux. It lets you fill in the various HTTP request fields and save off requests for future use.That said, the benefit of service virtualization is in enabling automated testing of individual microservices. That’s what Mountebank is all about.

 

 

That’s all for this article.


For more, check out the whole book on liveBook here and see this Slideshare Presentation.


[1] To avoid carpal tunnel syndrome, you can download the source at github.com/bbyars/mountebank-in-action.