Setting up and tearing down a test database helps keep tests clean and reliable. It makes sure each test starts fresh without leftover data.
Test database setup and teardown in Express
beforeAll(async () => { // Connect to test database }); afterAll(async () => { // Disconnect and clean up }); beforeEach(async () => { // Prepare fresh data }); afterEach(async () => { // Clear data });
beforeAll runs once before all tests start.
afterAll runs once after all tests finish.
beforeAll(async () => { await db.connect('mongodb://localhost/testdb'); });
afterAll(async () => { await db.disconnect(); });
beforeEach(async () => { await db.clearCollections(); });
This example uses an in-memory MongoDB to run tests without a real database. It connects before tests, clears data before each test, and disconnects after all tests. It shows adding and fetching users to confirm setup works.
import express from 'express'; import request from 'supertest'; import mongoose from 'mongoose'; import { MongoMemoryServer } from 'mongodb-memory-server'; const app = express(); app.use(express.json()); const userSchema = new mongoose.Schema({ name: String }); const User = mongoose.model('User', userSchema); app.post('/users', async (req, res) => { const user = new User({ name: req.body.name }); await user.save(); res.status(201).json(user); }); app.get('/users', async (req, res) => { const users = await User.find(); res.json(users); }); let mongoServer; beforeAll(async () => { mongoServer = await MongoMemoryServer.create(); const uri = mongoServer.getUri(); await mongoose.connect(uri); }); afterAll(async () => { await mongoose.disconnect(); await mongoServer.stop(); }); beforeEach(async () => { await User.deleteMany(); }); // Example test (async () => { // Add a user await request(app).post('/users').send({ name: 'Alice' }).expect(201); // Get users const res = await request(app).get('/users').expect(200); console.log(res.body); // Clear users await User.deleteMany(); // Get users again const res2 = await request(app).get('/users').expect(200); console.log(res2.body); })();
Use an in-memory database like mongodb-memory-server for fast, isolated tests.
Always clear data before each test to avoid test interference.
Disconnect and stop the database after tests to free resources.
Setup and teardown keep tests independent and reliable.
Use beforeAll and afterAll to manage database connection.
Use beforeEach and afterEach to reset data.
Practice
Solution
Step 1: Understand test database role
The test database is used to isolate test data from real production data to avoid accidental changes.Step 2: Purpose of setup connection
Setting up a connection before tests ensures all tests use this separate database environment.Final Answer:
To ensure tests run against a separate database without affecting real data -> Option BQuick Check:
Test DB isolates data = A [OK]
- Confusing test DB with production DB
- Thinking setup speeds app
- Assuming setup deploys app
Solution
Step 1: Recall Mongoose connection methods
Mongoose uses the methoddisconnect()to close the connection properly.Step 2: Verify method names
Methods likeclose(),end(), orstop()do not exist on the mongoose object for disconnecting.Final Answer:
mongoose.disconnect() -> Option DQuick Check:
Disconnect method is disconnect() = B [OK]
- Using nonexistent methods like close()
- Confusing disconnect() with close()
- Forgetting to await disconnect in async tests
beforeAll(async () => {
await mongoose.connect('mongodb://localhost/testdb');
});
afterAll(async () => {
await mongoose.disconnect();
});
test('database is empty before test', async () => {
const count = await User.countDocuments();
expect(count).toBe(0);
});What will the test output if the test database has leftover users from previous tests?
Solution
Step 1: Analyze test setup and test code
The test connects to the test database but does not clear data before running the test.Step 2: Effect of leftover data
If leftover users exist,User.countDocuments()returns a number greater than zero, causingexpect(count).toBe(0)to fail.Final Answer:
Test fails because count is not zero -> Option AQuick Check:
Leftover data causes count > 0 = D [OK]
- Assuming test DB auto-clears data
- Expecting count to be zero without cleanup
- Ignoring async connection setup
afterEach(async () => {
await mongoose.connection.dropDatabase();
await mongoose.disconnect();
});What is the main problem with this teardown approach?
Solution
Step 1: Understand teardown timing
Callingmongoose.disconnect()after each test closes the connection, but next tests expect it open.Step 2: Effect on subsequent tests
Subsequent tests will fail because the database connection is closed and not reopened automatically.Final Answer:
Disconnecting after each test causes connection errors in next tests -> Option CQuick Check:
Disconnect once after all tests = C [OK]
- Disconnecting inside afterEach instead of afterAll
- Assuming dropDatabase clears connection
- Forgetting async/await keywords
1. Connect once before all tests
2. Clear data before each test
3. Disconnect once after all tests
Why is this pattern preferred?
Solution
Step 1: Analyze connection management
Connecting once before all tests avoids overhead of reconnecting repeatedly, improving speed.Step 2: Analyze data clearing strategy
Clearing data before each test ensures tests do not affect each other, keeping them isolated and reliable.Step 3: Analyze disconnect timing
Disconnecting once after all tests properly closes resources without interrupting test flow.Final Answer:
It keeps connection open for speed and clears data to isolate tests -> Option AQuick Check:
Connect once, clear each test, disconnect once = A [OK]
- Disconnecting too often causing errors
- Not clearing data between tests
- Reconnecting for every test slowing tests
Ready to prove it?
Pass the test and this pattern moves unknown
