@nickglenn/mongo-seeder
v2.0.1
Published
Test utility for seeding MongoDB with application data.
Downloads
1
Maintainers
Readme
NodeJS Mongo Seeders for Testing
This library provides methods for quickly creating utilities for inserting seeded data into MongoDB for testing purposes. It supports several methods for inserting one to many rows, applying "patch" data on top of seed generated data, and clean up for seeded data.
Getting Started
Install the library via npm
. It's recommended that you install this as a development dependency.
npm i -D @nickglenn/mongo-seeder
Once installed, you can start integrating it by creating a "seeder map". This requires an object where the key
represents a collection in your database, and the value
is a factory function that returns the data necessary for a single document. It's recommended that you randomize the data output from each factory function in the map as this will help to create more robust tests.
Note: In the example below, we use the
faker
library to create some randomized data for our tests.
import { createSeederMap } from "@nickglenn/mongo-seeder";
import * as faker from "faker";
export const setupSeeders = createSeederMap({
users: () => ({
email: faker.internet.email(),
password: faker.internet.password(),
}),
posts: () => ({
title: faker.lorem.words(3),
content: faker.lorem.sentences(5),
published: faker.random.boolean(),
}),
});
Now let's add it to our test suite. The createSeederMap
function returned another function that needs us to provide an instance of a Mongo Database class. Once we've done that, we can begin using our seeders in some tests.
Note: The following syntax is what you'd likely see in a Jest or Mocha suite.
import * as mongo from "mongodb";
import { setupSeeders } from "./mySeeders";
import { findUserByEmail } from "./some/path/in/app";
let seeders;
beforeAll(async () => {
// connect to mongodb
const conn = await mongo.connect("mongodb://localhost/testing");
// instantiate our seeders
seeders = setupSeeders(conn.db());
});
afterEach(async () => {
// after each test we can destroy all seeded data generated by each test
await seeders.clean();
});
it("correctly fetches a user based on email", async () => {
// use any of the seeder methods to insert some test records
const testUser = await seeders.users.one();
// run your code
const fetchedUser = await findUserByEmail(testUser.email);
// perform assertions
expect(testUser._id.equals(fetchedUser._id)).toBe(true);
});
Seed Patching
type SeedPatcher<T> = Partial<T> | (generated: T, index: number) => T;
All of the methods for inserting seeds into the database support "seed patching". This allows generated records to be augmented in order to support specific test case scenarios.
Patch via Object
Patching seeds using an object performs a deep merge of the patch value on top of the generated value. This allows you to override the generated data before it's put into the database.
Using the example from above, we could alter our test to use a specific email address.
it("correctly fetches a user based on email", async () => {
// use any of the seeder methods to insert some test records
const testUser = await seeders.users.one({ email: "[email protected]" });
// run your code
const fetchedUser = await findUserByEmail("[email protected]");
// perform assertions
expect(testUser._id.equals(fetchedUser._id)).toBe(true);
});
Patch via Function
Sometimes a bit more flexibility is needed for testing scenarios. A patching function is called with the generated seed value (and numeric index) and the returned value is what will be stored in the database before being returned.
it("correctly fetches a user based on email from a larger pool of users", async () => {
// create 5 user records, but only the second record will have the test email
const testUsers = await seeders.users.many(5, (user, index) => {
if (index === 1) {
return { ...user, email: "[email protected]" };
}
return user;
});
// run your code
const fetchedUser = await findUserByEmail("[email protected]");
// perform assertions
expect(testUsers[0]._id.equals(fetchedUser._id)).toBe(false);
expect(testUsers[1]._id.equals(fetchedUser._id)).toBe(true);
expect(testUsers[2]._id.equals(fetchedUser._id)).toBe(false);
expect(testUsers[3]._id.equals(fetchedUser._id)).toBe(false);
expect(testUsers[4]._id.equals(fetchedUser._id)).toBe(false);
});
Seeder Methods
one
Seeder<T>.one(patch?: SeedPatcher<T>): Promise<T>;
Creates and inserts a single record into the database, returning the inserted record.
many
Seeder<T>.many(count: number, patch?: SeedPatcher<T>): Promise<T[]>;
Creates and inserts 1-n records into the database, returning the inserted records as an array. The argument count
cannot be less than 1
and must be an integer value.
random
Seeder<T>.random(min: number, max: number, patch?: SeedPatcher<T>): Promise<T[]>;
Creates and inserts a random number of records (within the given range) into the database, returning the inserted records as an array. The min
argument must be 0
or greater and the max
argument must be 1
greater than min
.
pick
Seeder<T>.pick(min: number, max: number, patch: SeedPatcher<T>): Promise<[ T, T[] ]>;
An amalgamation of the other seeder records, pick
actually requires that a seed patcher be provided. This is because pick will create a random number of records (within the given range) and insert them into the database However, a randomly selected record will be selected as the standout
and will be patched, while the other records will not be patched and are stored in a crowds
array.
This is useful for testing operations that may have differing results based on the amount of "noise" in the database.
const [standout, crowd] = await seeder.users.pick(min: 2, max: 20, { email: "[email protected]" });
clean
Seeder<T>.clean(): Promise<void>;
Deletes all inserted records created by the seeder instance.