Giter Site home page Giter Site logo

vanilli's Introduction

vanilli

Build Status

Nodejs library that allows managing of a RESTful server for storing, matching and verifying stubs/expectations from a test suite.

API

How it works

Vanilli is designed to act as a "fake" version of the REST services that your SUT (System Under Test) depends on. It sits running on a port you specify, waiting to serve up responses that you specify via adding stubs. Stubs are added and verified via the javascript API.

Your SUT is then configured to call vanilli instead of the REST services it usually uses.

Installation

npm install vanilli

Initialisation and configuration

Vanilli configuration can be specified when initalising:

var vanilli = require('vanilli');
...
vanilli.listen(port, config);

config is an optional parameter. All config options are given below with their default values:

{
    logLevel: "error", // See 'Diagnostics' section below
    static: undefined // See 'Static Content' section below
}

Usage

Typical usage:

var vanilli = require('vanilli');

describe('My SUT', function () {
    before(function () {
        vanilli.listen(port); // Start the vanilli REST server
    });

    after(function () {
        vanilli.stop(); // Shutdown vanilli REST server
    });

    afterEach(function () {
        vanilli.verify(); // Verify all expectations have been met
        vanilli.clear(); // Clear down stubs from vanilli ready for next test
    });

    it('does something', function (done) {
        vanilli.stub(
            vanilli.onGet("/this/url/MIGHT/happen").respondWith(200)
        );

        vanilli.expect(
            vanilli.onGet("/this/url/MUST/happen").respondWith(200)
        );

        // Manipulate SUT to required state

        // Make assertions

        // Note that the vanilli expectation above will be verified by the vanilli.verify() in 'afterEach'.
    });
});

See the API for more usage information.

Lazy matching

Vanilli's matching logic is lazy - i.e. a as long as ALL the criteria on a given stub match an incoming request vanilli does not care about any further details of that request. So, for example, if one specifies a stub that matches on a specific query parameter then the matching logic ONLY cares about that query parameter - any other query parameters are considered irrelevant.

This approach means more succinct stubs and less matching criteria irrelevant to the test at hand.

JSONP

Vanilli stub responses will automatically be wrapped in JSONP if either a "callback" or "jsonp" query string parameter is found on the request that the stub response is being produced for. This is not explicitly handled in vanilli but by its underlying restify server instead.

Diagnostics

Vanilli logs to sysout and syserr via bunyan. Switching logLevel to debug will cause vanilli to spit out a whole load of diagnostic information relating to what stubs are stored and how it is matching stubs against incoming requests. In such situations I recommend piping the output to the bunyan executable itself (which you can install in the usual way with npm install -g bunyan) to get nicely formatted output.

See the bunyan project itself for more info on logging and log levels.

Stubs vs expectations

For vanilli, an "expectation" is simply a specialized stub. In short: a stub MIGHT be matched; an expectation MUST be matched.

A stub...

  • CAN be matched UP TO the specified number of times (any number of times if not explicitly specified).
  • WILL cause an error if matched too many times.
  • WILL NOT cause an error if matched too few times.

An expectation...

  • MUST be matched the specified number of times (1 if not explicitly specified).
  • WILL cause a verification error if matched too many times.
  • WILL cause a verification if matched too few times.

So, if you want to assert on the actual calls that your SUT is using use an expectation; otherwise use a stub.

IMPORTANT Note that, unlike a stub, an expectation will ALWAYS silently match any number of times regardless of the value of times - it is only at the point of verification that any discrepancy is highlighted. So, it is important to include a call to verify in your "after each" handler in your test suite.

REMEMBER: The more vanilli expectations you add to your tests the more brittle they will get: consider using stubs as your first choice.

Static Content

To serve up the stubbed responses vanilli is, at its core, an HTTP server. This means that it could, potentially, serve up content other than stubs. This opens up the possibility of vanilli acting as your web app's HTTP server.

So, to this end, the static config option was created. It acts like a "pass through" filter - if an incoming request matches the static filter then the static content will be served rather than attempting to match against a stub. The option takes the form:

{
    static: {
        root: "sut/static/root", // Root path for static content 
        "default": "page.html", // Default document from root
        include: [
            "**/*.html", // normal include
            { path: '/foo', target: 'foo.html' }, // proxy route to specified target
            { path: '/foo/**', target: 'foo-sub.html' }, // proxy wildcard route to specified target
            { path: '/bar', useDefault: true } // proxy route to default "page.html"
        ],
        exclude: [ globA, globB, ... , globZ ]
    }
}

You can see an example in test/e2e/vanilli-test.js

CLI

As well as the javascript API described above, the vanilli server also provides a CLI for starting a server from non-javascript environments. To see the CLI simply run vanilli --help. (You may need to install vanilli globally first with npm install -g vanilli.)

vanilli's People

Contributors

kelveden avatar danscotton avatar bitdeli-chef avatar joelittlejohn avatar marcbachmann avatar

Stargazers

 avatar  avatar  avatar Jani Mikkonen avatar

Watchers

Kevin West avatar James Baum avatar Tom Coupland avatar Antonio Aloisio avatar  avatar Tom avatar  avatar Diego Colantoni avatar James Cloos avatar Mark Swaffer avatar Luke Snape avatar Tim Greene avatar Jon Mulligan avatar Adewale Adeyemi avatar Dean Hayes avatar shine paul avatar Alastair Brayne avatar Dan Stone avatar Ashley Best avatar Ben Lacey avatar Alex Watkinson avatar Rabeb Othmani avatar Mark Hearton avatar Miroslav Kouril avatar Taavi Kelle avatar Stu Mackellar avatar Alex Murray avatar James Wherlock avatar Mike Bell avatar Simon Robinson avatar David Harris-James avatar Benjamin avatar Xueli Jin avatar Pekka Tulokas avatar Steve Penn avatar Katherine Gould avatar Venkataramana Tirumalagiri avatar Lizanne Fernandez avatar Simon Bull avatar Jonathan Prem avatar Valerie Frost avatar Mary Bowden avatar  avatar Waldemar Zubik avatar Chuen Tsang avatar Christopher Myhill avatar Andrew Neale avatar Katie Cookson avatar Mark O'Grady avatar Stu Harvey avatar Alex Whittle avatar Andrew Douglas avatar Usha Manjunath avatar Nichoas Lovell avatar Dave Gatehouse avatar rich brooks avatar Elin Ahlberg avatar Pinky Shetty avatar Jo Gardiner avatar Yingze Wang avatar David Hobbs avatar Paolo Sammut avatar Donna Kelford avatar  avatar  avatar Lloney Monono avatar  avatar Bindu Priya Konidala avatar Simon Ness avatar Nick Aspinall avatar David Kendall avatar James Pearson avatar So Ting Tsang avatar  avatar  avatar Naomi Rusher avatar

Forkers

danscotton

vanilli's Issues

Vanilli rejecting requests that have URL encoded query parameters

Vanilli seems to have issues with URI encoded query parameters (created using encodeURIComponent):
It stores the stub (with encoded URLs in the query parameter) - then rejects the request with the reason that the request uses unencoded URLs in the parameter, but in the next line it complains about an unexpected request with encoded URLs.

See the following example:

Stored stub: {"criteria":{"method":"GET","url":"/test","query":{"urls":"http%3A%2F%2Flocalhost%2F2.jpg,http%3A%2F%2Flocalhost%2F3.jpg"}},"respondWith":{"status":200,"body":{"checked":["http://localhost/2.jpg","http://localhost/3.jpg"],"errored":[]},"contentType":"application/json"},"matched":0,"id":"cd7e5a6e-d9a6-46f8-87c4-b1516eb35e66"}

REJECTED stub 'cd7e5a6e-d9a6-46f8-87c4-b1516eb35e66' on query param 'urls' [Expected: 'http%3A%2F%2Flocalhost%2F2.jpg,http%3A%2F%2Flocalhost%2F3.jpg'; Actual: 'http://localhost/2.jpg,http://localhost/3.jpg']

UNEXPECTED request: GET /test?urls=http%3A%2F%2Flocalhost%2F2.jpg,http%3A%2F%2Flocalhost%2F3.jpg

If I stub the request with unencoded URLs it works fine:

Stored stub: {"criteria":{"method":"GET","url":"/test","query":{"urls":"http://localhost/2.jpg,http://localhost/3.jpg"}},"respondWith":{"status":200,"body":{"checked":["http://localhost/2.jpg","http://localhost/3.jpg"],"errored":[]},"contentType":"application/json"},"matched":0,"id":"cc91617d-b975-459d-b0ae-beb987006926"}

Add ability to queue stubs

AS A test writer
I WANT TO be able to queue stubs
SO THAT I can have my two (or more) stubs with identical criteria return different responses - one first, the other second.

Add ability to throttle vanilli responses

AS A tester
I WANT TO throttle vanilli stub responses
SO THAT I can test latency handling in my SUT

So, add something like:

respondWith: {
    wait: 2000
}

To force vanilli to only respond with this stub after waiting 2s.

Include rejection log output in 404 responses for stubs

AS A test writer
I WANT TO be able to see the actual rejection process that Vanilli has gone through on the HTTP requests in my tests
SO THAT I can determine why my requests weren't matching with the stubs defined in Vanilli without having to dig around the Vanilli logs themselves.

Need support for JSONP

The JSONP callback function itself will most likely not be known as it will be assigned internally by the JSONP implementation. Therefore, we need a way in vanilli to specify that a JSON callback should be used to pad around a stub response from a specific query string parameter. e.g. a stub might have:

{
    responseWith: {
        ...
        jsonpParam: "jsonp"
        ...
        body : { myfield: "value1" }
    }
}

If the url "/my/url?jsonp=somefunc" matched this stub, the resulting response would be:

somefunc({ myfield: "value1" });

with a "application/javascript" content type if none is explicitly specified.

Implement CORS properly

CORS support is a bit half-arsed - it essentially just opens up vanilli for access by anything on a different port. Perhaps that's actually enough. Still could do with a bit of a review.

Add console output of honoured/unexpected requests

AS A tester
I WANT TO be able to see what requests have been honoured and, more importantly, have NOT been honoured by vanilli
SO THAT I can more easily diagnose problems AND create tests that don't have unnecessary stubbing

Provide simpler priority values

Most of the time, when someone has to specify a priority for a stub, it's to have a "default" stub (probably setup in some sort of beforeEach function) and then overriding stubs in individual tests. To do this, one sets the priority of the "default" stub to some arbitrarily "high" value. The stub setup in the test then just needs a lower priority (or just don't specify the priority at all to imply 0) to be preferred over the "default" stub.

A more semantically descriptive approach would be good. Something like { priority: "low" }. Or perhaps even a replacement for the stub method on the API - stubFallback( ... ) that would imply a "high" value priority value (i.e. "low priority").

Body matching is too lenient

If the body criteria is something like "some data" then the stub will match any request body that contains "some data". The regex match should be across the entire body.

Basically vanilli needs to be smarter than it is - currently it just treats any body criteria as a regex whether it actually is or not. We need to be able to explicitly specify that the criteria is a regex. (Similar story for matching headers and urls too.)

Add proper node .bin support

AS A user
I WANT TO be able to start vanilli via CLI

So, after installing vanilli via npm, one can start vanilli up simply with: vanilli.

Allow easy specification of stubs matching "any" times

So, something like, onGet("/some/url").respondWith(200).anyTimes().

Currently the stub-registry stores the times field as a number so we'll need a number that can be used to represent "any" - I'd suggest -1 rather than 0. This is because we could use 0 in the future to represent an expectation that must NOT be met.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.