Allow users to create mocks using 2 APIs... Consumers or providors could write mocks using these tools...
// This block only runs if recording live
await ifLiveBlock(async () => {
await new Promise(resolve => {
const pipe = spawn("mongod", ["--dbpath=<LOCATION>", "--port", "1223"]);
pipe.stdout.on("data", function(data) {
if (data.indexOf("started") !== -1) {
resolve();
}
});
});
});
// if CLI flag / env var is set to record, this returns the live server results
// if not, it returns a snapshotted version. When live the above ifLiveBlock would have
// started the server it would connect to making the process seamless
const results = await memorized("Mocked query for bar", async () => {
const client = await mongodb.connect({ port: 1223 });
return client.query({ foo: "bar" });
});
// In the above call to memorizeMock we have a name, a function that conditionaly called
// if we are "recording" or the snapshotted response if playing back.
Pros:
- one test/mock file, and one step... No need to write a test that generates the mocks first, allowing it to feel a lot more like Jest snapshots.
- This not only allows for tests to be run as unit tests... but if the CI flag is on also as intigration tests. All with a single test being written!
- No setup to ensure is working localy or a CI... enviorment starting or not is not programaticly controlled in the test itself
That's the API... in practice it would look more like this:
///////////////////////
// user.test.js
///////////////////////
test("user creation works", () => {
const user = require("../user");
return user.create("mitchell", 22);
});
///////////////////////
// ./__mocks__/user.js
///////////////////////
const user = jest.genMockFromModule("../user");
// If the tests using this mock are live, this runs, otherwise it does not
await ifLiveBlock(async () => {
await new Promise(resolve => {
const pipe = spawn("mongod", ["--dbpath=<LOCATION>", "--port", "1223"]);
pipe.stdout.on("data", function(data) {
if (data.indexOf("started") !== -1) {
resolve();
}
});
});
});
// If live, this returns data from the live mongodb server,
// otherwise it returns from the memorized data saved to a snapshot
user.create = (name, age) => {
return await memorized(`create-user`,
async () => {
const mongodb = require('mongodb');
const client = await mongodb.connect({ port: 1223 });
return await client.insert({ name, age });
}, [...args]
// ˆˆ matches all args to the snapshot data
);
};
module.exports = user;
If memorized/ifLiveBlock use an env var to toggle live vs mocked data, then this becomes Test framework agnostic.
This API/setup also means I can make use of all the advantages of this regardless of if I am the providor OR the consumer of the mocks, meaning I don't need to get buy-in from the entire team or company to use it.
The ifLiveBlock is something that works exactly the same. I will use it for initiation logic and it is a good idea for many other cases.
So if I translate it into Mockshot, it will be almost the same.
Now Mockshot will use AST to generate an actual class and create
mocks/UserService.ts
that looks something like this:That means, that I can take this and simply merge it with the real class using isLiveBlock.
There are actually many ways to do it cleanly, it is easy to do automatically and pretty cool: