I usually write tests with a
Given, When, Then structure to
keep the test focused on documenting and confirming the important behaviour of
whatever is being tested. Simple comments are good for this and provide an
easy-to-follow visual structure:
test("some defined behaviour", () => {
// Given the fandango is combobulated;
// When we discombobulate the fandango;
// Then the fandango should be discombobulated.
})
It’s important to keep tests clear and readable. Reducing the number of lines
under each step and making each line as meaningful as possible are the main ways
to do that. You can use
test factories and other
extractions to reduce line-count and make test code more meaningful.
Another approach I’ve been using in tests is to extract each step as a suitably
named step function, so that the test ends up looking like this:
test("some defined behaviour", () => {
// Given there is a combobulated fandango;
const fandango = await thereIsACombobulatedFandango()
// When we discombobulate the fandango;
await fandangoDiscombobulator.discombobulateFandango(fandango.id)
// Then the fandango should be discombobulated.
await theFandangoHasBeenDiscombobulated(fandango.id)
})
async function thereIsACombobulatedFandango(): Promise<Fandango> {
return makeFandango({ combobulated: true })
}
async function theFandangoHasBeenDiscombobulated(
fandangoId: string
): Promise<void> {
// ...some async work...
expect(fandango.combobulated).toBe(false)
}
The extracted steps are trivial, but when there is more realistic complex work
happening in each step, this improves the readability of the test. Each step is
now meaningful and relevant to confirming the behaviour we’re interested in
here.
A lot of testing frameworks, especially BDD ones, have this kind of reusable
test step. However, the potential for re-use isn’t actually the main goal or
benefit here. The main benefit of doing this is to make the test code more
meaningful.
Notice that the step functions are not exported – we’re primarily interested in
extracting semantic steps for this test alone. If we happen to be able to re-use
the same steps elsewhere, that could be a nice side-benefit, but we’d need to be
careful not to let them attract cruft as more and more logic gets added to the
most popular test steps. Keeping straightforward test steps right next to the
test they serve is usually more beneficial.
View post:
A simple, readable, meaningful test style with Jest
|