8/5/2022, 10:40:46 AM
This is a living document, it will be updated from time to time as my test writing evolves.
It's easy to fall into the trap of writing your tests with less care than you would your regular code. After all, if tests pass, then ship it is de rigeur, so there's an incentive — especially if the clock is ticking closer to the end of workday — to get those tests passing ASAP.
One corner that is often cut is to introduce or reuse variables between tests. For example:
The problem with falling into this trap is that you're relying on the state/value of a variable that is not tightly scoped.
7
, so I will assert.strictEqual(7, uid);
. However if additional tests are created above mine, and another user is created, then uid
could become 8
and this unrelated test would fail.describe.only()
), but the tests fail because the state of earlier variables is different.If testing temporary entities (a user account, some data object, etc.) — create a new one for testing, and use it only in the context of that test.
define('user tests', () => {
let uid;
before(async () => {
uid = await createUser();
});
... tests go here...
});
In the example above, uid
is scoped to the top level define()
, so additional tests will not have access to that uid. Scope tighter as necessary, follow the Principle of Least Privilege.
Every test (or at least every block of related tests) should be able to be run independently. If they fail when run exclusively, then there is an issue with leaking state.
If mutating global state (e.g. application config), it is easy to fall into the trap of not cleaning up after yourself. For example, if you are testing the logic of the foo
feature, you can turn it on, run your tests, and leave it on.
The same failure cases apply — side effects, and test failures due to unrelated regressions.
If possible, clean the slate completely (database wipe), or as close to as possible. Failing that, clean up after yourself by unsetting configurations you've set, and changing back configs you've changed.
define('foo feature', () => {
before(() => {
global.foo = true;
});
after(() => {
global.foo = false;
});
});