I'm trying to test a couple of database implementations using Jest. To help test these implementations, I first came up with a set of unit tests against an API that both implementations are expected to implement.
I'm currently struggling with passing the two implementations to the test suites.
Below is a (dummy) MongoDB implementation in its simplest form:
class MongoDB {
async query () {
console.warn(`This is a dummy function.`)
}
async connect () {
// The real connect takes some time..instead we just simulate it
await new Promise((resolve, reject) => {
setTimeout(resolve, 300)
})
}
}
And here's a small snippet of my tests:
let db
beforeAll(async () => {
db = new MongoDB()
await db.connect()
console.log(`mongoDB ready`)
})
async function testDB (db) {
describe('Basic', async () => {
test('Valid instance', async () => {
expect(db).toBeTruthy()
expect(db.query).toBeTruthy()
})
})
}
describe('Tests', async () => {
console.log(`Running testDB`)
testDB(db) // Have also unsuccessfully tried changing this to: return testDB(db)
})
My goal with this approach is to wrap all my tests inside the testDB
function and simply call it with various implementations. For example, testDB(new MongoDB())
and testDB(new MemoryDB())
and so on.
This doesn't seem to work as expected, however. The above code results in an error stating that:
● Tests › Basic › Valid instance
expect(received).toBeTruthy()
Expected value to be truthy, instead received
undefined
The order of the console.log
statements seems to suggest that the tests are running before db
was initialized.
console.log mongo.test.js:20
Running testDB
console.log mongo.test.js:7
mongoDB ready
This entire example along with the resulting output can be reproduced on repl.it.
How can I reuse unit tests to test multiple implementations without resorting to duplicating the tests and maintaining two versions?
Faced same need today. Here is the way, adapted from typescript, but you get the idea:
// common/service.test.js
export const commonServiceTests = (name, impl) => {
describe(`Common tests for ${implName}`, () => {
// pile your tests here
test('test1', () => { ... });
test('test2', () => { ... });
test('test3', () => { ... });
});
}
// just to avoid warning, that no tests in test file
describe('Common tests for CommonService implementations', () => {
test('should be used per implementation', () => {});
});
And for your each implementation:
// inmemory/service.test.js
import { commonServiceTests } from '../common/service.test';
import ...; // your implementation here
const myInMemoryService = ...; // initialize it
commonServiceTests('InMemory', myInMemoryService);
Then all tests defined in common/service.test.js
will be executed within each implementation test.
In case your initialization is async
(which is most likely), then your shared tests should be async
as well. Then:
// common/service.test.js
export const commonServiceTests = (name, impl: Promise) => {
describe(`Common tests for ${implName}`, () => {
// pile your async tests here
test('test1', async () => {
const svc = await impl;
return await svc.doSomthingPromisy();
});
test('test2', () => { ... });
test('test3', () => { ... });
});
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With