This gist is an example of how unit tests could be written for the Wormholecash
repo. The example below is taken from the balancesForAddress
test in the
dataRetrieval.js
test file.
The original test looks like this:
// This is technically an 'integration' test because it makes a live HTTP call.
describe("#balancesForAddress", () => {
it(`original (integration) test`, async () => {
let balancesForAddress = await Wormhole.DataRetrieval.balancesForAddress(
"bchtest:qq2j9gp97gm9a6lwvhxc4zu28qvqm0x4j5e72v7ejg"
);
//console.log(`balancesForAddress: ${JSON.stringify(balancesForAddress,null,2)}`);
assert.deepEqual(Object.keys(balancesForAddress[0]), ["propertyid", "balance", "reserved"]);
});
});
One of the big distinctions between unit and integration tests is that unit tests don't rely on any external services, like a databases or external servers. The test above would be classified as an integration test. Both tests are important, as are end-to-end tests. Ideally, reliable software has all three.
Here are my take on unit tests. While sinon and mockery are great ways to mock out libraries, for http calls, I like to use nock. I use it below to mock out the HTTP calls without having to mock out axios directly.
let chai = require("chai");
let assert = require("assert");
const nock = require("nock");
let wh = require("./../lib/Wormhole").default;
let Wormhole = new wh({
restURL: "https://wormholecash-staging.herokuapp.com/v1/",
});
describe("#DataRetrieval", () => {
// This is a 'unit' test because it mocks the http request and requires no external service to run the test.
it(`unit test`, async () => {
// https://wormholecash-staging.herokuapp.com/v1/dataRetrieval/balancesForAddress/bchtest:qq2j9gp97gm9a6lwvhxc4zu28qvqm0x4j5e72v7ejg
nock(`https://wormholecash-staging.herokuapp.com`)
.get(
`/v1/dataRetrieval/balancesForAddress/bchtest:qq2j9gp97gm9a6lwvhxc4zu28qvqm0x4j5e72v7ejg`
)
.reply(200, [
{
propertyid: 1,
balance: "67.65779501",
reserved: "0.00000000",
},
]);
let balancesForAddress = await Wormhole.DataRetrieval.balancesForAddress(
"bchtest:qq2j9gp97gm9a6lwvhxc4zu28qvqm0x4j5e72v7ejg"
);
assert.deepEqual(Object.keys(balancesForAddress[0]), ["propertyid", "balance", "reserved"]);
});
// The advantage of unit tests is that you can respond with a variety of errors,
// like this 503 HTTP error.
it(`503 unit test`, async () => {
// https://wormholecash-staging.herokuapp.com/v1/dataRetrieval/balancesForAddress/bchtest:qq2j9gp97gm9a6lwvhxc4zu28qvqm0x4j5e72v7ejg
nock(`https://wormholecash-staging.herokuapp.com`)
.get(
`/v1/dataRetrieval/balancesForAddress/bchtest:qq2j9gp97gm9a6lwvhxc4zu28qvqm0x4j5e72v7ejg`
)
.reply(503, "error");
try {
await Wormhole.DataRetrieval.balancesForAddress(
"bchtest:qq2j9gp97gm9a6lwvhxc4zu28qvqm0x4j5e72v7ejg"
);
assert.equal(false, true, "Test passing unexpectedly!");
} catch (err) {
assert.equal(err.response.status, 503, "503 error expected.");
}
});
});
It would be good to add a few more test cases to a test like this. Like the ECONNREFUSED error in this issue or returning a rejected promise.
Another way to do these same tests is to use assert.throws()
, but there is a strong case
to be made in the testing industry for writing positive and negative tests. The
last test would be considered a negative test, by putting the assertion in the catch
.
throws is a fine way to do it too,
but I find this format makes the most sense to me.