I’ve been working on a project that needed some integration tests. Before any tests in a suite run, I wanted to open a connection to the database and do some other setup. At the end of the suite, I wanted to close the connection and tear down cleanly.
Jest has beforeAll
and afterAll
hooks that run before and after all tests in a test suite, which
is basically what I wanted. But I don’t want to copy and paste the same code into every test file.
I could of course just export some shared functions, but anytime someone adds a new test they have
to remember to implement beforeAll
and afterAll
correctly. That seems annoying: I really wanted
a “mixin” that I could easily bring in that would automatically apply the hooks to the test suite.
I stumbled across this post from Kristian Dupont that held the solution I wanted! Thanks Kristian!
The idea copies the pattern of React hooks. You can define a custom useTestFixture
function that
sets up the beforeAll
and afterAll
callbacks and returns a test fixture.
Since the fixture is actually instantiated in the beforeAll
hook, you wind up having to return
a function that fetches the fixture. This is a little clunky but I think the tradeoff is worth it.
I have my hook return an instance of a custom class that has useful helper methods for more easily writing the tests.
Here’s a representative sample:
1 |
|
My real-life beforeAll
hook is a bit more full-featured. It includes customizations to:
- Start up the mongo database client
- Start up the express server
- Start up the socket.io server
- Set up mocks for AWS SDK calls
- Make sure the test run gets a new virtual user, and that the user document exists in the database
My actual TestFixture
class contains a bunch of useful helpers, such as
- inserting and fetching users
- making requests using supertest
- A promisified wrapper around socket.io-client’s
emit
operation - shutting everything down cleanly
- Nice methods to set up common mocks, such as
signInAsAdmin
To use it, just import the hook and call it in your test file, inside the top-level describe block:
1 | describe('UserController', () => { |
When tests are easy to write, engineers are more likely to write them. If they have to write a lot of fiddly setup code in each test, they just throw up their hands and poke at it in the browser. To encourage writing tests, it’s important to provide frameworks that make it trivial to implement high-value test suites.
This trick is a great way to do that. It papers over the fiddly setup bits and provides testing utilities that reduce how much engineers have to write.