just the right amount of coverage
Tools like nock and sinon make it easy to write rich, complete simulations of a REST API or an HTTP web service, but not easy enough for the likes of us. These tools are more suited to integration testing or end-to-end testing. When you're writing small unit tests, you shouldn't have to spin up a server and declare a route, just to make something that replies to a GET with an HTTP 418. Even a famously concise tool like Express seems too imperative and wordy for these common cases. Why not slip into something cool, casual and comfortable?
var assert = require('assert');
var request = require('request');
var jort = require('jort');
jort({
totalCount: 2,
items: ["daisy","duke"]
}).then(function(twoItems) {
request(twoItems, function(error, response, json) {
assert.equal(json.totalCount, 2);
assert.equal(json.items[0], "daisy");
});
});
Jort has features, but they're hidden from view, in deep, embroidered pockets; the simple version you just saw above assumes a few things.
The jort
function, the main interface to jort, takes one argument: an object which will be the response. It must be a plain object, serializable as JSON, because that's what will happen to it.
var coupleNicknames = {
nickiAndMeek: ["Meeki", "Neck"],
drakeAndLorde: ["Drorde", "Lake"]
};
var nicknamesResponder = jort(coupleNicknames);
The function will return a Promise for a string. It'll be something like https://localhost:8099/
, but you don't have to care. Call that URL once. It will respond HTTP 200 OK, with appropriate headers for a JSON response, return that response, and then disappear.
nicknamesResponder.then(function(nicknamesResponse) {
request(nicknamesResponse, function(error, response, json) {
json.drakeAndLorde.map(console.log);
/*
* Drorde
* Lake
*
*/
});
});
You can pass two arguments to jort()
, one for each leg the first as the payload, similar to above. The second argument is a collection of options. I wonder when we'll find out what they can be!
var emptyPocket = jort(
/* payload */
{
errorCode: "NOT_FOUND",
message: "The requested document was not found, hoss!"
},
/* options */
{
status: 404,
delay: 20000
}
);
status
: an integer for the HTTP status code to return. Default200
headers
: an object of http headers to add or override, e.g.{ 'Content-Type': 'text/html' }
delay
: an integer of ms to delay the first byte of the response. For testing timeouts. Default0
use
: an array of connect middleware to use (or a single function to be used as one middleware)leaveOpen
: by default, every jort will only serve one request. Set this totrue
to leave the jort running until you turn it off. Of course, you can't turn it off unless you've calledjort.serve
orjort.serveSteps
to get an actual server handle, so this option is non-functional unless you called one of those methods. Your Jort takes care of you.ipv6
: by default, jort will use ipv6 where available, so if your system supports ipv6, jort will produce urls that look likehttps://[::]:8000/
. If you don't like this (or another library doesn't recognize this as a valid URL), then you can pass an explicitipv6:false
to force ipv4-style urls.port
: by default, jort will find an open, unused port using portfinder, unless you specify a port with this option; then it will use that port.
If the first argument is a JS object, Jort will assume it should be serving JSON. If the first argument is a string, Jort will pass the string through unchanged (and you ought to provide a content type header in that case).
Not very jorty of you, but you can get this by running jort.serve
instead of just jort
. jort.serve
has the exact same API as jort
.
The returned Promise will fulfill an object which has two properties: url
will be the url to call, just as if you'd called jort
, and server
will be the Node http.Server. The server has already been bound to a port, so you don't have to do that manually. It'll also close by itself unless you passed leaveOpen: true
as an option.
var bananaHammock = jort.serve("Bananas!", { headers: { 'Content-Type': 'text/bananas' } });
bananaHammock.then(function(bananas) { console.log(bananas.url, bananas.server.address()) });
// https://127.0.0.1:8001
// { protocol: 'http:', address: '127.0.0.1', port: 8001 }
If it get more complicated than this, you'll want to switch to just making your own express server, but ok, here goes. jort has a method called steps
that will return a promise for a URL, just like jort, but this URL will respond a little differently. jort.steps
takes an array of arguments that you might normally send to jort
.
It yields a URL that will respond with the payloads of each of these arguments, in order, and different behavior based on the arguments. this must be an array of arrays, each sub-array representing the arguments that you might normally send directly to jort()
. if you don't need a second options
argument, then it can be an array of payloads; each item in the array will be interpreted as a single payload with no options if it is not an array, and a payload/options tuple if it is an array. Therefore, if you want your response to be a JSON array, it just needs to be an array wrapped in an array. Hey, you asked for a complicated feature in an oversimplified tool!
jort.steps([
/* first request, for authentication */
[
/* payload */
{
authToken: 'fake-auth-token',
issued: (new Date()).toISOString()
},
/* options for first request */
{
delay: 1000,
headers: {
"X-Custom-Greeting": "Cheers!"
}
}
],
/* second request, for metadata */
[
{
totalCount: 20000,
publishState: 'draft'
}
/* no options */
],
/* third request, for data */
[
/* payload is array */
[
{
firstName: 'Sam',
lastName: 'Malone',
occupation: 'Barman'
},
{
firstName: 'Diane',
lastName: 'Chambers',
occupation: 'Student/Barmaid'
}
],
{
delay : 5000
}
]
],
/* global options that will apply to the server or to all requests*/
{
// `leaveOpen: true` could go here
}).then(function(url) {
// the url will respond three times, each time with the next payload,
// and then disappear into the eldritch mist.
fetch(uri).then(function(res) {
assert(res.authToken);
return fetch(uri);
}).then(function(res) {
assert(res.totalCount === 10000);
return fetch(uri);
}).then(function(res) {
assert(res[o].firstName === "Sam");
});
});
The leaveOpen
option will, of course, not work for individual request options, but all other options will. The base options object you supply after the steps array will be applied to every step, but will be overridden by any options present for that step.
You can call jort.serveSteps
to get a steps
implementation that will return a server, instead of just a url. It has the same API as jort.steps
, of course.
That's probably not what you're testing. Your app knows how to use the right HTTP verbs, I'm sure. Don't sell yourself jort.
I agree that this is a problem, but it doesn't seem like a problem I can solve for you.
![jorts in repose](https://i.imgur.com/LONbcQT.jpg)