TypeScript & Test Fixtures
March 11, 2019
When setting up the environment for which a test should run in, test fixtures allow you to encapsulate the context set-up in a way which allows your tests to be executed in a repeatable way. I find them particularly important when working with systems at scale with a large data store or a large amount of domain objects which can take a long time to hydrate with values. Once you start to create more of them for the domain objects within your application it aids in the speed at which you set up the context of your test as they become both re-usable and composable.
When I write fixture functions for domain objects within TypeScript applications I generally follow the following rules:
The function takes one optional input, the domain object it will return which it will use to override generated values. This allows the author to override values which have importance within the fact or theory of the scenario of the test. TypeScript makes this super easy to type too with the Partial type along with using object spread syntax. The function returns a single object conforming to the shape of the type you’re creating. The function makes use of a fake data generator (I personally use faker.js). An example fixture function could be seen by the following:
interface User {
readonly id: number;
readonly firstName: string;
readonly lastName: string;
readonly emailAddress: string;
readonly postCode?: string;
}
const createUser = (user?: Partial<User>): User => ({
id: faker.random.number(),
firstName: faker.random.word(),
lastName: faker.random.word(),
emailAddress: faker.random.word(),
postCode: faker.random.word(),
...user
})
const user = createUser({
firstName: 'Daniel'
})
Making use of TypeScript’s Partial type here makes it really easy to ensure type safety whilst being able to override properties which might have importance within the scenario you’re building.
I find that making use of random values for primitive data types allows you to express your test scenarios in a way which still conforms to specification by example but extending your tests capabilities input range bringing you closer to property based testing.
When I was working with the C# programming language I really enjoyed working with a library named AutoFixture which provided me with test data generators for both primitive and complex data types, this made it incredibly easy for me to set-up the context of a test which was dependent on a large amount of domain objects as it would handle the generation of these objects recursively with reflection. The same idea could potentially be applied to TypeScript & JavaScript.
I'm a software engineer with a various range of experience with different technologies. I really enjoy functional programming and teaching others.