Skip to content

HTTP stub server written in Java with embedded Jetty

License

Notifications You must be signed in to change notification settings

kaizer/stubby4j

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

stubby4j

A stub HTTP server written in Java with embedded Jetty server

Why the word "stubby"?

It is a stub HTTP server after all, hence the "stubby". Also, in Australian slang "stubby" means beer bottle

Table of Contents

Why would a developer use stubby4j?

####You want to:

  • Simulate responses from real server and don't care (or cannot) to go over the network
  • Verify that your code makes HTTP requests with all the required parameters and/or headers
  • Verify that your code correctly handles HTTP error codes
  • You want to trigger response from the server based on the request parameters over HTTP or HTTPS
  • Support for any of the available HTTP methods
  • Simulate support for Basic Authorization
  • Support for HTTP 30x redirects
  • Support for regular expressions (like mod_rewrite in Apache) in stubbed URIs for dynamic matching
  • Trigger multiple responses based on multiple requests on the same URI
  • Configure stub data using configuration file
  • Configure stub data at runtime, without restarting the server by making a POST to an exposed endpoint
  • Live tweak previously loaded and parsed configuration file to auto refresh the stub data WITHOUT restarting the server
  • Provide canned answers in your contract/integration tests
  • Enable delayed responses for performance and stability testing
  • Avoid to spend time coding for the above requirements
  • Concentrate on the task at hand

Why would a QA use stubby4j?

  • Specifiable mock responses to simulate page conditions without real data.
  • Ability to test polling mechanisms by stubbing a sequence of responses for the same URI
  • Easily swappable data config files to run different data sets and responses.
  • All-in-one stub server to handle mock data with less need to upkeep code for test generation
All this goodness in just under 1.5MB

Building

stubby4j is a multi-module Gradle project. IntelliJ IDEA users should run gradle cleanIdea idea in order to generate IntelliJ IDEA project files. Eclipse users should run cleanEclipse eclipse in order to generate Eclipse project files.

Run gradle command to:

  • Clean
  • Build

Run gradle testAll command to:

  • Clean
  • Run unit, integration and functional tests in the specified order without Cobertura

Run gradle clean cobertura command to:

  • Generate Cobertura report under the main module

Dependencies

stubby4j is a fat JAR, which contains the following dependencies:

  • commons-cli-1.2.jar
  • snakeyaml-1.11.jar
  • javax.servlet-3.0.0.v201112011016.jar
  • jetty-server-8.1.7.v20120910.jar
  • jetty-continuation-8.1.7.v20120910.jar
  • jetty-util-8.1.7.v20120910.jar
  • jetty-io-8.1.7.v20120910.jar
  • jetty-http-8.1.7.v20120910.jar

stubby4j is also compatible with Jetty 7.x.x and servlet API v2.5

Maven Central

stubby4j is hosted on Maven Central and can be added as a dependency in your POM. Check Maven Central for the latest version of stubby4j

<dependency>
    <groupId>by.stub</groupId>
    <artifactId>stubby4j</artifactId>
    <version>x.x.xx</version>
</dependency>

Command-line Switches

java -jar stubby4j-x.x.xx.jar [-a <arg>] [-d <arg>] [-h]
       [-k <arg>] [-l <arg>] [-m] [-p <arg>] [-s <arg>] [-t <arg>] [-w]
 -a,--admin <arg>      Port for admin portal. Defaults to 8889.
 -d,--data <arg>       Data file to pre-load endpoints. Valid YAML 1.1
                       expected.
 -h,--help             This help text.
 -k,--keystore <arg>   Keystore file for custom SSL. By default SSL is
                       enabled using internal keystore.
 -l,--location <arg>   Hostname at which to bind stubby.
 -m,--mute             Prevent stubby from printing to the console.
 -p,--password <arg>   Password for the provided keystore file.
 -s,--stubs <arg>      Port for stub portal. Defaults to 8882.
 -t,--ssl <arg>        Port for SSL connection. Defaults to 7443.
 -w,--watch            Reload datafile when changes are made.

Endpoint Configuration

This section explains the usage, intent and behavior of each property on the request and response objects.

Here is a fully-populated, unrealistic endpoint:

-  request:
      url: ^/your/awesome/endpoint$
      method: POST
      query:
         exclamation: post requests can have query strings!
      headers:
         content-type: application/xml
      post: >
         <!xml blah="blah blah blah">
         <envelope>
            <unaryTag/>
         </envelope>
      file: tryMyFirst.xml

   response:
      status: 200
      latency: 5000
      headers:
         content-type: application/xml
         server: stubbedServer/4.2
      body: >
         <!xml blah="blah blah blah">
         <responseXML>
            <content></content>
         </responseXML>
      file: responseData.xml

request

This object is used to match an incoming request to stubby against the available endpoints that have been configured.

url (required)

  • is a full-fledged regular expression
  • This is the only required property of an endpoint.
  • signify the url after the base host and port (i.e. after localhost:8882).
  • must begin with /.
  • any query paramters are stripped (so don't include them, that's what query is for).
    • /url?some=value&another=value becomes /url
  • no checking is done for URI-encoding compliance.
    • If it's invalid, it won't ever trigger a match.

This is the simplest you can get:

-  request:
      url: /

A demonstration when not using regular expressions:

-  request:
      url: /some/resource/that/will/be/fully/matched

A demonstration using regular expressions:

-  request:
      url: ^/has/to/begin/with/this/

-  request:
      url: /has/to/end/with/this/$

-  request:
      url: ^/must/be/this/exactly/with/optional/trailing/slash/?$

-  request:
      url: ^/[a-z]{3}-[a-z]{3}/[0-9]{2}/[A-Z]{2}/[a-z0-9]+$

method

  • defaults to GET.
  • case-insensitive.
  • can be any of the following:
    • HEAD
    • GET
    • POST
    • PUT
    • POST
    • DELETE
    • etc.
-  request:
      url: /anything
      method: GET
  • it can also be an array of values.
-  request:
      url: /anything
      method: [GET, HEAD]

-  request:
      url: /anything
      method:
         -  GET
         -  HEAD

query

  • if omitted, stubby ignores query parameters for the given url.
  • a yaml hashmap of variable/value pairs.
  • allows the query parameters to appear in any order in a uri
  • query params can also be an array with double/single quoted/un-quoted elements: attributes=["id","uuid"] or attributes=[id,uuid]. Please note no spaces between the CSV
-  request:
      method: GET
      url: ^/with/parameters$
      query:
         type_name: user
         client_id: id
         client_secret: secret
         attributes: '["id","uuid","created","lastUpdated","displayName","email","givenName","familyName"]'
  • The following will match either of these:
    • /with/parameters?search=search+terms&filter=month
    • /with/parameters?filter=month&search=search+terms
-  request:
      url: ^/with/parameters$
      query:
         search: search terms
         filter: month

post

  • if ommitted, any post data is ignored.
  • the body contents of the server request, such as form data.
-  request:
      url: ^/post/form/data$
      post: name=John&email=john@example.com

file

  • if supplied, replaces post with the contents of the locally given file.
    • an absolute path or path relative to the YAML specified -d or --data
  • if the file is not found when the request is made, falls back to post for matching.
  • allows you to split up stubby data across multiple files
  • please keep in mind: SnakeYAML library (used by stubby4j) parser ruins multi-line strings by not preserving system line breaks. If file property is stubbed, the file content is loaded as-is, in other words - it does not go through SnakeYAML parser. Therefore its better to load big POST content for request using file property. Keep in mind, stubby4j stub server is dumb and does not use smart matching mechanism (ie:. don't match line separators or don't match any white space characters) - whatever you stubbed, must be POSTed exactly for successful match
-  request:
      url: ^/match/against/file$
      file: postedData.json
      post: '{"fallback":"data"}'

postedData.json

{"fileContents":"match against this if the file is here"}
  • if postedData.json doesn't exist on the filesystem when /match/against/file is requested, stubby will match post contents against {"fallback":"data"} (from post) instead.

headers

  • if ommitted, stubby ignores headers for the given url.
  • case-insensitive matching of header names.
  • a hashmap of header/value pairs similar to query.

The following endpoint only accepts requests with application/json post values:

-  request:
      url: /post/json
      method: post
      headers:
         content-type: application/json

response

Assuming a match has been made against the given request object, data from response is used to build the stubbed response back to the client.

  • Can be a single response or a sequence of responses.
  • When sequenced responses configured, on each request to a URI, a subsequent response in the list will be sent to the client. The sequenced responses play in a cycle (loop). In other words: after the response sequence plays through, the cycle restarts on the next request.
-  request:
      method: [GET,POST]
      url: /invoice/123

   response:
      status: 201
      headers:
         content-type: application/json
      body: OK


-  request:
      method: [GET]
      url: /uri/with/sequenced/responses

   response:
      -  status: 201
         headers:
            content-type: application/json
         body: OK

      -  status: 201
         headers:
            content-stype: application/json
         body: Still going strong!

      -  status: 500
         headers:
            content-type: application/json
         body: OMG!!!



-  request:
      method: [GET]
      url: /uri/with/single/sequenced/response

   response:
      -  status: 201
         headers:
            content-stype: application/json
         body: Still going strong!

status

  • the HTTP status code of the response.
  • integer or integer-like string.
  • defaults to 200.
-  request:
      url: ^/im/a/teapot$
      method: POST
   response:
      status: 420

body

  • contents of the response body
  • defaults to an empty content body
-  request:
      url: ^/give/me/a/smile$
   response:
      body: ':)'

file

  • similar to request.file, but the contents of the file are used as the body.
  • if the file was not provided, stubby fallsback to value from body property
  • if body was not provided, an empty string is returned by default
  • can be ascii of binary file (PDF, images, etc.). Please keep in mind, that file is preloaded upon stubby4j startup and its content is kept in byte array in memory. In other words, response files are not read from the disk on demand, but preloaded.
-  request:
      url: /
   response:
      file: extremelyLongJsonFile.json

headers

  • similar to request.headers except that these are sent back to the client.
-  request:
      url: ^/give/me/some/json$
   response:
      headers:
         content-type: application/json
      body: >
         [{
            "name":"John",
            "email":"john@example.com"
         },{
            "name":"Jane",
            "email":"jane@example.com"
         }]

latency

  • time to wait, in milliseconds, before sending back the response
  • good for testing timeouts, or slow connections
-  request:
      url: ^/hello/to/jupiter$
   response:
      latency: 800000
      body: Hello, World!

The Admin Portal

The admin portal is a RESTful(ish) endpoint running on localhost:8889. Or wherever you described through stubby's options.

Supplying Endpoints to Stubby

Submit POST requests to localhost:8889 or load a data-file (-d) with the following structure for each endpoint:

  • request: describes the client's call to the server
    • method: GET/POST/PUT/DELETE/etc.
    • url: the URI regex string. GET parameters should also be included inline here
    • query: a key/value map of query string parameters included with the request
    • headers: a key/value map of headers the server should respond to
    • post: a string matching the textual body of the response.
    • file: if specified, returns the contents of the given file as the request post. If the file cannot be found at request time, post is used instead
  • response: describes the server's response to the client
    • headers: a key/value map of headers the server should use in it's response
    • latency: the time in milliseconds the server should wait before responding. Useful for testing timeouts and latency
    • file: if specified, returns the contents of the given file as the response body. If the file cannot be found at request time, body is used instead
    • body: the textual body of the server's response to the client
    • status: the numerical HTTP status code (200 for OK, 404 for NOT FOUND, etc.)

YAML (file only)

-  request:
      url: ^/path/to/something$
      method: POST
      headers:
         authorization: "Basic usernamez:passwordinBase64"
      post: this is some post data in textual format
   response:
      headers:
         Content-Type: application/json
      latency: 1000
      status: 200
      body: You're request was successfully processed!

-  request:
      url: ^/path/to/anotherThing
      query:
         a: anything
         b: more
      method: GET
      headers:
         Content-Type: application/json
      post:
   response:
      headers:
         Content-Type: application/json
         Access-Control-Allow-Origin: "*"
      status: 204
      file: path/to/page.html

-  request:
      url: ^/path/to/thing$
      method: POST
      headers:
         Content-Type: application/json
      post: this is some post data in textual format
   response:
      headers:
         Content-Type: application/json
      status: 304

If you want to load more than one endpoint via file, use YAML list (-) syntax.

Getting the Current List of Stubbed Endpoints

Performing a GET request on localhost:8889/yaml will return a YAML of all currently saved responses. It will reply with 204 : No Content if there are none saved.

The Status Page

You can also view the currently configured endpoints by going to localhost:8889/status

The Stubs Portal

Requests sent to any url at localhost:8882 (or wherever you told stubby to run) will search through the available endpoints and, if a match is found, respond with that endpoint's response data

How Endpoints Are Matched

For a given endpoint, stubby only cares about matching the properties of the request that have been defined in the YAML. The exception to this rule is method; if it is omitted it is defaulted to GET.

For instance, the following will match any POST request to the root url:

-  request:
      url: /
      method: POST
   response: {}

The request could have any headers and any post body it wants. It will match the above.

Pseudocode:

for each <endpoint> of stored endpoints {

   for each <property> of <endpoint> {
      if <endpoint>.<property> != <incoming request>.<property>
         next endpoint
   }

   return <endpoint>
}

Programmatic API

To be added soon ...

Change Log

2.0.2 - SNAPSHOT

  • URI for registering new stub data programatically via POST on Admin portal was changed from /stubdata/new to / [COSMETICS]
  • URI for getting loaded stub data status was changed from /ping to /status on Admin portal [COSMETICS]
  • Added new endpoint on Admin portal /yaml to display the loaded stub data in a YAML format in the browser [ENHANCEMENT]
  • When YAML is parsed, if file could not be loaded, the IOException is not thrown anymore. Instead a warning recorded in the terminal [ENHANCEMENT]
  • Documentation refinement [COSMETICS]

2.0.1

  • Every url is treated as a regular expression now [ENHANCEMENT]
  • ANSI logging in the terminal was working only for HTTP requests with status 200 [BUG]
  • Documentation refinement [COSMETICS]

2.0.0

  • Mainly backend code improvements: A lot of refactoring for better code readability, expanding test coverage [COSMETICS]

1.0.63

  • Added ability to specify sequence of stub responses for the same URI, that are sent to the client in the loop [FEATURE]
  • Configuration scan was not enabled, even if the --watch command line argument was passed [BUG]

1.0.62

  • Added ability to specify regex in stabbed URL for dynamic matching [FEATURE]
  • A lot of minor fixes, refactorings and code cleaned up [COSMETICS]
  • Documentation revisited and rewritten into a much clearer format [ENHANCEMENT]

1.0.61

  • Just some changes around unit, integration and functional tests. Code cleanup [COSMETICS]

1.0.60

  • stubby's admin page was generating broken hyper links if URL had single quotes [BUG]
  • stubby is able to match URL when query string param was an array with elements within single quotes, ie: attributes=['id','uuid'] [ENHANCEMENT]

1.0.59

  • stubby's admin page was not able to display the contents of stubbed response/request body, post or file [BUG]
  • stubby was not able to match URL when query string param was an array with quoted elements, ie: attributes=["id","uuid","created","lastUpdated","displayName","email"] [BUG]

1.0.58

  • Making sure that stubby can serve binary files as well as ascii files, when response is loaded using the file property [ENHANCEMENT]

1.0.57

  • Migrated the project from Maven to Gradle (thanks to Logan McGrath for his feedback and assistance). The project has now a multi-module setup [ENHANCEMENT]

1.0.56

  • If request.post was left out of the configuration, stubby would ONLY match requests without a post body to it [BUG]
  • Fixing See Also section of readme [COSMETICS]

1.0.55

  • Updated YAML example documentation [COSMETICS]
  • Bug fix where command line options mute, debug and watch were overlooked [BUG]

1.0.54

  • Previous commit (v1.0.53) unintentionally broke use of embedded stubby [BUG]

Authors

A number of people have contributed directly to stubby4j by writing documentation or developing software.

  1. Alexander Zagniotov azagniotov@gmail.com
  2. Eric Mrak enmrak@gmail.com

Kudos

A number of people have contributed to stubby4j by reporting problems, suggesting improvements or submitting changes. Special thanks fly out to the following Ninjas for their help, support and feedback

  • Isa Goksu
  • Eric Mrak
  • Oleksandr Berezianskyi
  • Sankalp Saxena
  • Simon Brunning
  • Ed Hewell
  • Kenny Lin
  • Logan McGrath

See Also

Copyright

See COPYRIGHT for details.

About

HTTP stub server written in Java with embedded Jetty

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Java 93.3%
  • XML 4.0%
  • Groovy 2.7%