Tests are improved by being more readable, and concise tests are more readable.
It’s good when a test has clear given, when, then steps, and the important
details are plain to see in the test code.
This is often obstructed by large amounts of set-up and boilerplate code in the
test that distracts from the important details.
Test model factories can reduce some of that boilerplate.
For example, if we have a Client model that contains quite a lot of detail,
setting one up in a test will be verbose. We might only be interested in testing
the effect of a single field in a single test, but setting up all the other
required fields obscures that intention.
It’s clearer if we only specify the interesting fields for this test:
// Given we have a client over the age of 18;
const client = makeClient({ biographic: { dateOfBirth: "1990-01-01" } });
// ... now it's clear what is important for the rest of the test.
This can be a bit tricky to do with strong typing in TypeScript, as you want to
enforce that the client is set up correctly, but also allow tweaking only the
particular fields that are relevant to a given test.
The built-in
Partial
utility type gets you most of the way there. With some help from StackOverflow,
we can get a RecursivePartial
type.
The factory function signature looks like this:
function makeClient(properties?: RecursivePartial<Client>): Client
This lets you strongly type the Client structure, and also allow typed but
individual overrides of particular parts. You can pass in just the properties
you want to override, or none at all and use all of the defaults.
The implementation is like this:
const faker = require('faker/locale/en_GB')
import * as _ from 'lodash'
function makeClient(properties?: RecursivePartial<Client>): Client {
const defaults: Client = {
firstName: faker.name.firstName(),
lastName: faker.name.lastName(),
biographic: {
dateOfBirth: moment.utc().subtract(Math.random() * 80 + 18, 'years')
}
}
const finalProperties = _.merge(defaults, properties)
const client: Client = new Client()
for (const key of Object.keys(finalProperties)) {
client[key] = finalProperties[key]
}
return client
}
Default valid client properties are set up using the
faker library. Then lodash merge is used
to replace any given oveßrrides from the passed in properties. The result is an
overridable client factory that allows for concise tests and maintains type
safety.
View post:
Flexible test model factories in TypeScript
|