In most situations Tape
will be exactly what you need
to write and run your Node.js/JavaScript tests.
Tape
is minimalist, fast and has flexibility when you need it.
Occasionally however, the needs of the project
require a few extra features from the testing framework and
that is when we use Tap
.
If you find yourself (your project) needing to do "setup"
before
running a test or "teardown" after
the test is complete
(e.g. resetting a mock or the state of a front-end app),
OR you have a few hundred/thousand tests which are taking a "long time"
to run (e.g: more than 10 seconds),
then you will benefit form switching/upgrading to Tap
.
The Tap
repo has quite a good breakdown of the reasons
why you should consider using Tap
:
https://github.com/tapjs/node-tap#why-tap
Tape
has 5x more usage thanTap
according to NPM install stats.
This is due to a number of factors including "founder effect".
But the fact thatTape
has fewer features has contributed to it's enduring/growing popularity.
Ensure you have usedTape
in at least one "real" projectbefore
consideringTap
, otherwise you risk complexity!
Tap
is a testing framework that
has a few useful extra features without being "bloated".
-
beforeEach
orafterEach
for "resetting state" between tests. - parallel test execution: https://www.node-tap.org/parallel/
- built-in code coverage: https://www.node-tap.org/coverage/
We will cover beforeEach
by adding a real world "coin supply"
to our vending machine example below.
Tap
has many more "advanced" features, if you are curious see: https://www.node-tap.org/advanced
But don't get distracted by the "bells and whistles", focus on building your App/Project and if you find that you are repeating yourself a lot in your tests, open an issue describing the problem e.g:
Continuing our theme of a "vending machine", let's consider the following real world use case: The Vending Machine "runs out" of coins and needs to be re-filled!
We will add this functionality to the calculateChange
function
and showcase a useful Tap
feature: beforeEach
Once those basics are covered,
we will dive into something way more ambitious!
Start by installing Tap
]
and saving it to your package.json
:
npm install tap -D
Copy the Tape
test
so we can repurpose it for Tap
:
cp test/change-calculator.test.js test/change-tap.test.js
Open the test/change-tap.test.js
file and change the first line
to require tap
instead of tape
.
From:
const test = require('tape');
To:
const test = require('tap').test;
Run the Tap
tests:
node test/change-tap.test.js
The output is slightly different from Tape
,
but the tests still pass:
In the "real world" the coins in the machine will be given out
as change to the people buying products and may "run out".
Therefore having a fixed Array of coins in the calculateChange
function
is artificially setting expectations that might not reflect reality.
We need a way of (optionally) passing
the array of coins into the calculateChange
function
but having a default array of coins
so the existing tests continue to pass.
Note: for brevity, we are using the functional solution to
calculateChange
function.
If this is unfamiliar to you, please see: https://github.com/dwyl/learn-tdd#functional
Add a new parameter to the JSDOC comment (above the function):
Before:
/**
* calculateChange accepts two parameters (totalPayable and cashPaid)
* and calculates the change in "coins" that needs to be returned.
* @param {number} totalPayable the integer amount (in pennies) to be paid
* @param {number} cashPaid the integer amount (in pennies) the person paid
* @returns {array} list of coins we need to dispense to the person as change
* @example calculateChange(215, 300); // returns [50, 20, 10, 5]
*/
After:
/**
* calculateChange accepts three parameters (totalPayable, cashPaid, coinsAvail)
* and calculates the change in "coins" that needs to be returned.
* @param {number} totalPayable the integer amount (in pennies) to be paid
* @param {number} cashPaid the integer amount (in pennies) the person paid
* @param {array} [coinsAvail=COINS] the list of coins available to select from.
* @returns {array} list of coins we need to dispense to the person as change
* @example calculateChange(215, 300); // returns [50, 20, 10, 5]
*/
The important bit is:
* @param {array} [coinsAvail=COINS] the list of coins available to select from.
[coinsAvail=COINS]
means:
- the argument will be called
coinsAvail
and - a default value of
COINS
(the default Array of coins) will be set if the argument is undefined. - the
[ ]
(square brackets) around the parameter definition indicates that it's optional.
More detail on optional parameters in JSDOC comments,
see: http://usejsdoc.org/tags-param.html#optional-parameters-and-default-values
The full file should now look like this:
lib/change-calculator.js
At this point nothing in the
calculateChange
function or tests has changed, so if you run the tests, you should see exactly the same output as above in step 4; all tests still pass.
Add the following test to your test/change-tap.test.js
file:
test('Vending Machine has no £1 coins left! calculateChange(1337, 1500, [200, 50, 50, 50, 10, 5, 2, 1]) should equal [100, 50, 10, 2, 1 ]', function (t) {
const result = calculateChange(1337, 1500, [200, 50, 50, 50, 10, 5, 2, 1]);
const expected = [50, 50, 50, 10, 2, 1 ]; // £1.63
t.deepEqual(result, expected,
'calculateChange returns the correct change');
t.end();
});
Run the Tap
tests:
node test/change-tap.test.js
You should see the test fail:
Now that we've written the JSDOC for our function,
let's add the coinsAvail
parameter to the calculateChange
function:
Before:
module.exports = function calculateChange (totalPayable, cashPaid) {
...
}
After:
module.exports = function calculateChange (totalPayable, cashPaid, coinsAvail) {
...
}
The tests will still not pass. Re-run them and see for yourself.
Given that the coinsAvail
parameter is optional we need
a default value to work with in our function,
let's create a coins
function-scoped variable
and use conditional assignment to set COINS
as the default value
of the Array if coinsAvail
is undefined:
Before:
module.exports = function calculateChange (totalPayable, cashPaid, coinsAvail) {
return COINS.reduce(function (change, coin) {
// etc.
}, []);
}
After:
module.exports = function calculateChange (totalPayable, cashPaid, coinsAvail) {
const coins = coinsAvail || COINS; // if coinsAvail param undefined use COINS
return coins.reduce(function (change, coin) {
// etc.
}, []);
}
This should be enough code to make the test defined above in step 5.2 pass.
Save your changes to the calculateChange
function and re-run the tests:
node test/change-tap.test.js
You should see all the tests pass:
That's it, you've satisfied the requirement of allowing a
coinsAvail
("Coins Available") array
to be passed into the calculateChange
function.
But hold on ... are we really testing the real-world scenario
where the coins are removed from the coinsAvail
("supply") each time
the Vending Machine gives out change...?
The answer is "No".
All our previous tests assume an infinite supply of coins
and the latest test simply passes in the array of coinsAvail
,
we are not removing the coins from the coinsAvail
Array.
In the "real world" we need to reduce the coins in the vending machine each time change is given.
Imagine that the vending machine starts out with a supply of 10 of each coin:
let COINS = [
200, 200, 200, 200, 200, 200, 200, 200, 200, 200,
100, 100, 100, 100, 100, 100, 100, 100, 100, 100,
50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1
];
This is a total of 80 coins; 8 coin types x 10 of each type.
Each time the machine returns coins we need to remove them from the supply otherwise we could end up giving the incorrect change because the machine may attempt to disburse coins which it does not have.
Let's create a new module/function to implement this requirement.
Note: for the purposes of this tutorial/example we are allocating 10 of each type of coin to the "supply".
If we want represent the actual coins in circulation, we would need to apportion coins according to their popularity,
see: https://en.wikipedia.org/wiki/Coins_of_the_pound_sterling#Coins_in_circulation
Create the new
file for the more "advanced" Vending Machine functionality:
atom lib/vending-machine.js
Following "Documentation Driven Development" (DDD), we write the JSDOC Documentation comment first and consider the function signature up-front.
/**
* reduceCoinSupply removes the coins being given as change from the "supply"
* so that the vending machine does not attempt to give coins it does not have!
* @param {number} cashPaid the integer amount (in pennies) the person paid
* @param {array} coinsAvail supply of coins to chose from when giving change.
* @param {array} changeGiven the list of coins returned as change.
* @returns {array} list of coins available in the vending machine
* @example reduceCoinSupply([200, 100, 50, 10, 1], [100, 50, 10]); // [200, 1]
*/
function reduceCoinSupply (coinsAvail, changeGiven) {
}
By now the JSDOC syntax should be familiar to you. If not, see: https://github.com/dwyl/learn-jsdoc
Document-Driven Development as an engineering discipline takes a bit of getting used to, but it's guaranteed to result in more maintainable code. As a beginner, this may not be your primary focus, but the more experienced you become as a engineer, the more you will value maintainability; writing maintainable code is a super power everyone can achieve!
Create the file test/vending-machine.test.js
and add the following code to it:
const test = require('tap');
const vendingMachine = require('../lib/vending-machine.js');
const reduceCoinSupply = vendingMachine.reduceCoinSupply;
tap.test('reduceCoinSupply([200, 100, 50, 10, 1], [100, 50, 10]); returns [200, 1]', function (t) {
const result = reduceCoinSupply([200, 100, 50, 10, 1], [100, 50, 10]);
const expected = [200, 1];
t.deepEqual(result, expected,
'reduceCoinSupply reduces the coinsAvail by the coins given as change');
t.end();
});
Save the file and run the test:
node test/vending-machine.test.js
You should see it fail:
In the lib/vending-machine.js
file,
write just enough code in the reduceCoinSupply
function body
to make the test pass.
Remember to export the function at the end of the file:
module.exports = {
reduceCoinSupply: reduceCoinSupply
}
Try to solve this challenge yourself (or in pairs/teams)
before looking at the "solution":
lib/vending-machine.js > reduceCoinSupply
When it passes you should see:
To "exercise" the reduceCoinSupply
function,
let's add another test example.
Add the following test to the test/vending-machine.test.js
file:
tap.test('reduceCoinSupply remove more coins!', function (t) {
const COINS = [
200, 200, 200, 200, 200, 200, 200, 200, 200, 200,
100, 100, 100, 100, 100, 100, 100, 100, 100, 100,
50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1
];
const remove = [
200, 200, 200, 200, 200, 200, 200, 200, 200,
100, 100, 100, 100, 100, 100, 100, 100,
50, 50, 50, 50, 50, 50, 50,
20, 20, 20, 20, 20, 20,
10, 10, 10, 10, 10,
5, 5, 5, 5,
2, 2, 2,
1, 1,
];
const expected = [
200,
100, 100,
50, 50, 50,
20, 20, 20, 20,
10, 10, 10, 10, 10,
5, 5, 5, 5, 5, 5,
2, 2, 2, 2, 2, 2, 2,
1, 1, 1, 1, 1, 1, 1, 1
];
const result = reduceCoinSupply(COINS, remove);
t.deepEqual(result, expected,
'reduceCoinSupply removes coins from supply of coinsAvail');
t.end();
});
You should not need to make any changes to your reduceCoinSupply
function.
Simply saving the test file and re-running the Tap tests:
node test/vending-machine.test.js
You should see the both tests pass:
So far so good, we have a way of reducing the coins available but there is still an "elephant in the room" ... how does the Vending Machine keep track of the coins it has available? i.e the "state" of the machine.
The vending machine needs to "know" how many coins it still has, to avoid trying to give coins it does not have available.
There are a number of ways of doing "state management". Our favourite is the Elm Architecture. We wrote a dedicated tutorial for it; see: https://github.com/dwyl/learn-elm-architecture-in-javascript
However to illustrate a less sophisticated state management we are using a globalCOINS
Array.
In your test/vending-machine.test.js
file add the following code:
tap.test('Check Initial Supply of Coins in Vending Machine', function (t) {
const COINS = [
200, 200, 200, 200, 200, 200, 200, 200, 200, 200,
100, 100, 100, 100, 100, 100, 100, 100, 100, 100,
50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1
];
t.deepEqual(vendingMachine.getCoinsAvail(), COINS,
'vendingMachine.getCoinsAvail() gets COINS available in Vending Machine');
vendingMachine.setCoinsAvail([1,2,3]);
t.deepEqual(vendingMachine.getCoinsAvail(), [1,2,3],
'vendingMachine.setCoinsAvail allows us to set the COINS available');
t.end();
});
If you run the tests,
node test/vending-machine.test.js
you will see the last one fail:
In lib/vending-machine.js
file,
after the reduceCoinSupply
function definition,
add the following variable declaration and pair of functions:
// GOLBAL Coins Available Array. 10 of each type of coin.
let COINS = [
200, 200, 200, 200, 200, 200, 200, 200, 200, 200,
100, 100, 100, 100, 100, 100, 100, 100, 100, 100,
50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1
];
/**
* setCoinsAvail a "setter" for the COINS Array
* @param {Array} coinsAvail the list of available coins
*/
function setCoinsAvail (coinsAvail) {
COINS = coinsAvail;
}
/**
* getCoinsAvail a "getter" for the COINS Array
*/
function getCoinsAvail () {
return COINS;
}
We will use the COINS
array
and both the "getter" and "setter"
in the next step.
For now, simply export "getter" and "setter" so the test will pass:
module.exports = {
reduceCoinSupply: reduceCoinSupply,
setCoinsAvail: setCoinsAvail,
getCoinsAvail: getCoinsAvail,
}
Re-run the test and watch it pass:
We already have a calculateChange
function
which calculates the change for a given amount of money received
and price of product being purchased,
and we have the reduceCoinSupply
function which removes coins
from the "supply" the vending machine has.
These can be considered "internal" functions.
Now all we have to do is combine these two functions into a "public interface" which handles both calculating change and disbursing the coins from the supply.
Add the following JSDOC comment and function signature to
the lib/vending-machine.js
file:
/**
* sellProduct accepts three parameters (totalPayable, coinsPaid and coinsAvail)
* and calculates the change in "coins" that needs to be returned.
* @param {number} totalPayable the integer amount (in pennies) to be paid
* @param {array} coinsPaid the list of coins (in pennies) the person paid
* @param {array} [coinsAvail=COINS] the list of coins available to select from.
* @returns {array} list of coins we need to dispense to the person as change
* @example sellProduct(215, [200, 100]); // returns [50, 20, 10, 5]
*/
function sellProduct (totalPayable, coinsPaid, coinsAvail) {
}
Things to note here:
the JSDOC and function signature
are similar to the calculateChange
function
the key distinction is the second parameter: coinsPaid
.
The vending machine receives a list of coins when the person makes a purchase.
In your test/vending-machine.test.js
file add the following code:
tap.test('sellProduct(215, [200, 100], COINS) returns [50, 20, 10, 5]', function (t) {
const COINS = vendingMachine.getCoinsAvail();
const coinsPaid = [200, 100];
const result = vendingMachine.sellProduct(215, coinsPaid, COINS);
const expected = [50, 20, 10, 5];
t.deepEqual(result, expected,
'sellProduct returns the correct coins as change');
// check that the supply of COINS Available in the vendingMachine was reduced:
t.deepEqual(vendingMachine.getCoinsAvail(), reduceCoinSupply(COINS, result),
'running the sellProduct function reduces the COINS the vending machine');
t.end();
});
Note: you will have noticed both from the JSDOC and the test invocation that the
sellProduct
function returns one array; the list of coins to give the customer as change. JavaScript does not have a "tuple" primitive (which would allow a function to return multiple values), so we can either return anObject
in thesellProduct
function orreturn
just the Array of coins to be given to the customer as change. The second assertion in the test shows that the COINS array in vendingMachine module is being "mutated" by thesellProduct
function. This is an undesirable "side effect" but this illustrates something you are likely to see in the "wild". If you feel "uncomfortable" with this "impure" style, and you should, consider learning a functional language like Elm, Elixir or Haskell JavaScript "works", but it's ridiculously easy to inadvertently introduce bugs and "unsafety". which is why sanctuary exists.
If you run the tests:
node test/vending-machine.test.js
you will see the last one fail:
The sellProduct
function will invoke the calculateChange
function,
so the first thing we need to do is import it.
At the top of the lib/vending-machine.js
file,
add the following line:
const calculateChange = require('./change-calculator.js');
Now we can implement sellProduct
which should only a few lines
invoking other functions.
Again, try implementing it yourself (or in pairs/teams),
before looking at the
vendingMachine.sellProduct
solution
Don't forget to export
the sellProduct
function.
During the execution of our last test for the sellProduct
function,
the COINS
array in the Vending Machine has been altered.
Let's write another test to illustrate this.
In your test/vending-machine.test.js
file add the following code:
tap.test('Check COINS Available Supply in Vending Machine', function (t) {
const coinsAvail = vendingMachine.getCoinsAvail()
t.equal(coinsAvail.length, 76,
'vendingMachine.getCoinsAvail() shows COINS in Vending Machine is ' +
coinsAvail.length);
t.end();
});
If you followed all the previous steps the test will pass,
meaning that the COINS
array in the vendingMachine
was modified by the previous (sellProduct
) test.
The initial state for the COINS
array
that we defined in step 5.6.2 (above)
was 80 Coins,
but after executing the sellProduct
test it's 76 coins.
It both simulates the "real world" and creates a testing "headache";
modifying the COINS
array (the vending machine's coins available "state")
is desirable because in the real world each time an item is sold
the state of COINS
should be updated.
But it means we need to "reset" the COINS
array between tests
otherwise we will end up writing coupled ("co-dependent") tests.
Given that the COINS
state in the vending machine is being
modified by the sellProduct
function,
and the fact that we don't want tests to be co-dependent,
we either need to "reset" the state of COINS
inside each test,
or we need to reset the state of COINS
before each test.
Here is the difference:
tap.test('Check COINS Available Supply in Vending Machine', function (t) {
const COINS = [
200, 200, 200, 200, 200, 200, 200, 200, 200, 200,
100, 100, 100, 100, 100, 100, 100, 100, 100, 100,
50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1
];
vendingMachine.setCoinsAvail(COINS);
const coinsAvail = vendingMachine.getCoinsAvail();
t.equal(coinsAvail.length, 80,
'vendingMachine.getCoinsAvail() shows COINS in Vending Machine is ' +
coinsAvail.length);
t.end();
});
tap.test('sellProduct(1337, [1000, 500], coinsAvail) >> [100, 50, 10, 2, 1]', function (t) {
const COINS = [
200, 200, 200, 200, 200, 200, 200, 200, 200, 200,
100, 100, 100, 100, 100, 100, 100, 100, 100, 100,
50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1
];
vendingMachine.setCoinsAvail(COINS);
let coinsAvail = vendingMachine.getCoinsAvail();
const expected = [100, 50, 10, 2, 1];
const result = vendingMachine.sellProduct(1337, [1000, 500], coinsAvail)
t.equal(result, expected,
'sellProduct(1337, [1000, 500], coinsAvail) is ' + result);
coinsAvail = vendingMachine.getCoinsAvail();
t.equal(coinsAvail.length, 75, // 80 minus the coins dispensed (5)
'vendingMachine.getCoinsAvail() shows COINS in Vending Machine is ' +
coinsAvail.length);
t.end();
});
As you can see these two tests share the same "setup" or "reset state" code:
const COINS = [
200, 200, 200, 200, 200, 200, 200, 200, 200, 200,
100, 100, 100, 100, 100, 100, 100, 100, 100, 100,
50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1
];
vendingMachine.setCoinsAvail(COINS);
const coinsAvail = vendingMachine.getCoinsAvail();
We can easily remove this identical duplication of code
by using one of Tap
's built-in testing helper functions: beforeEach
!
We can re-write the two preceding tests
and eliminate the duplication,
by using the Tap
beforeEach
function
to reset the test before each test:
tap.beforeEach(function (done) { // reset state of COINS before each test:
const COINS = [
200, 200, 200, 200, 200, 200, 200, 200, 200, 200,
100, 100, 100, 100, 100, 100, 100, 100, 100, 100,
50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1
];
vendingMachine.setCoinsAvail(COINS);
done()
});
tap.test('Check COINS Available Supply in Vending Machine', function (t) {
const coinsAvail = vendingMachine.getCoinsAvail();
t.equal(coinsAvail.length, 80,
'vendingMachine.getCoinsAvail() shows COINS in Vending Machine is ' +
coinsAvail.length);
t.end();
});
tap.test('sellProduct(1337, [1000, 500], coinsAvail) >> [100, 50, 10, 2, 1]', function (t) {
let coinsAvail = vendingMachine.getCoinsAvail();
const expected = [100, 50, 10, 2, 1];
const result = vendingMachine.sellProduct(1337, [1000, 500], coinsAvail)
t.deepEqual(result, expected,
'sellProduct(1337, [1000, 500], coinsAvail) is ' + result);
coinsAvail = vendingMachine.getCoinsAvail();
t.equal(coinsAvail.length, 75, // 80 minus the coins dispensed (5)
'vendingMachine.getCoinsAvail() shows COINS in Vending Machine is ' +
coinsAvail.length);
t.end();
});
Both of these approaches will give the same result:
But the beforeEach
approach has an immediate benefit in terms of
reduction of duplicate code in tests
which is less to read (reduces cognitive load)
and easier to maintain (if the "reset state" code needs to be changed,
it's changed in one place).
If you found this taster on testing with Tap
helpful, consider reading our more comprehensive Todo List example:
https://github.com/dwyl/todomvc-vanilla-javascript-elm-architecture-example .
To the untrained observer,
Tape
is like a single speed bicycle;
lightweight, fewer "moving parts", less to learn and fast!
Perfect for short trips on relatively flat terrain.
Most journeys in cities fit this description.
Most of the time you won't need anything more than this
for commuting from home to work, going to the shops, etc.
Tap
is the bicycle with gears
that allows you to tackle different terrain.
"Gears" in the context of writing unit/end-to-end tests
is having a t.spawn
(run tests in a separate process),
or running tests in parallel so they finish faster;
i.e. reducing the effort required to cover the same distance
and in some cases speeding up the execution of your test suite.
Note: This analogy falls down if your commuting distance is far; you need a geared bicycle for the long stretches! Also, if you never ride a bicycle - for whatever reason - and don't appreciate the difference between single speed and geared bikes this analogy might feel less relevant ... in which case we recommend a bit of background reading: https://bicycles.stackexchange.com/questions/1983/why-ride-a-single-speed-bike
One of the benefits of Free/Open Source software is that there is near-zero "switching cost". Since we aren't paying for the code, the only "cost" to adopting a new tool is learning time.
Given that Tap
is a "drop-in replacement" for Tape
in most cases, the switching cost is just npm install tap -D
followed by a find-and-replace across the files in your project from:
require('tape')
to require('tap').test
.
Over the last 5 years @dwyl we have tested 200+ projects using Tape
(both Open Source and Client projects).
We have no reason for "complaint" or criticism of Tape
,
we will not be spending time switching
our existing projects from Tape
to Tap
because in most cases, there is no need;
YAGNI!
Where we are using Tap
is for the massive projects
where we either need to do a lot of state re-setting e.g: t.afterEach
or simply where test runs take longer than 10 seconds
(because we have lots of end-to-end tests)
and running them in parallel significantly reduces waiting time.
For an extended practical example of where writing tests with Tap
instead of Tape
was worth the switch,
see: https://github.com/dwyl/todomvc-vanilla-javascript-elm-architecture-example .