Right now, I’m working with one of X-Team’s partners to help them take their architecture and development stack to the next level.
One of our tasks is to provide a scalable testing environment for a number of projects which are currently under heavy development. You can think about it as an ecosystem containing multiple products sharing similar objects from the same business domain. It was only natural to model our testing environment in the same way, sharing the testing data across different specifications.
The challenges with testing data
How can we maintain test data which would reflect an actual production environment (both in terms of complexity and numbers)? The three most prominent challenges are:
- Sharing the same data structures across different testing approaches (BDD, E2E, Unit testing, Integration testing, Functional testing). Name it and you can probably test it.
- Making data random. There are times when our “John Doe” user is not enough to test border cases.
- Making generated data grow in numbers. When testing maps, listings, or grids, generating each object by hand is too time consuming; we need to get them in bulk.
Approaching generation and scaling
Addressing the first challenge, sharing the same data structure, is rather obvious: we need a common repository for objects. Maintaining folders with files containing JSON objects will suffice for most usecases.
When it comes to the second point – randomness – we can call the mighty Faker.js to the rescue. This small library lets you generate objects with randomized fields. Feel free to use predefined generators, such as names, emails, addresses, card numbers or, when you need something fancier, create your own generators.
We addressed the third point, by introducing a separate tool able to generate large numbers of randomised objects on demand: The Fixture Factory. It is a node package based on Faker.js syntax which provides you with a way to register object blueprints and generate massive numbers of randomised data based on them. Let’s see it in action.
Fixture Factory
Installation
npm install fixture-factory --save-dev
Generating a simple user object
var fixtureFactory = require('fixture-factory');
//create definition which will be used as a blueprint in factory
var userBlueprint = {
firstName: 'name.firstName',
lastName: 'name.lastName',
email: 'internet.email',
city: 'address.city',
country: 'address.country'
};
//register it for general use
fixtureFactory('user', userBlueprint);
Please note that in the example above we are using fixture factory as a global service. Blueprints can be registered and accessed from anywhere as long as we have access to fixtureFactory object (singleton). The only thing left is to generate randomized user (or ten of them).
var fixtureFactory = require('fixture-factory');
fixtureFactory.generateOne('user');
//will return single JSON object
fixtureFactory.generate('user', 10);
//will return array of 10 objects
Still – we can do better than that. Let’s use some fancier Faker.js methods (f.e. random number) and add a field based on generated fixture.
var fixtureFactory = require('fixture-factory');
var userBlueprint = {
age: {
method: 'random.number',
//let's pass some options around
options: {
min: 18,
max: 90
}
},
//you can even have fields based on other generated fields, how cool is that?
ageCategory: function(fixture) {
return fixture.age > 18 ? 'adult' : 'minor';
}
};
fixtureFactory.register('user', userBlueprint);
Sometimes you may need a special type of object (in this case, an admin user) for your tests.
var fixtureFactory = require('fixture-factory');
fixtureFactory.generateOne('user', {
role: 'admin'
});
//will return standard user object with injected role: 'admin' field
or to postprocess generated object
var fixtureFactory = require('fixture-factory');
var userBlueprint = {
firstName : 'name.firstName'
};
fixtureFactory.register('user', userBlueprint);
fixtureFactory.generateOne('user', {
firstName: function (fixture, options, dataModel, faker) {
return 'sir '+ fixture.firstName
}
});
//will generate user object but with overwritten firstName field
The final thing worth mentioning is the ability to use fixture factory as source for single generators, rather than as a global service. So instead of registering and generating user blueprints through a Fixture Factory singleton we may get access to simple generator.
var fixtureFactory = require('fixture-factory');
//create definition which will be used as a blueprint for generator methods
var userBlueprint = {
firstName: 'name.firstName',
lastName: 'name.lastName',
email: 'internet.email',
city: 'address.city',
country: 'address.country'
};
//register it and get a generator
var userFactory = fixtureFactory('user', userBlueprint).getGenerator('user');
//generate
userFactory.generateOne();
userFactory.generate(10);
Fixture Factory is available as open source. Have fun faking.