Skip to main content

Integration Testing

While Unit Tests prove that a single brick is strong, Integration Tests prove that the mortar (the glue) holds the bricks together to form a wall.

In a typical CodeHarborHub backend, this means testing if your Service Layer can successfully talk to your Database or an External API.

🧐 The "Why" Behind Integration Tests

You might have a perfectly working User object and a perfectly working Database. But if the User object expects a firstName and the Database table is named first_name, your app will crash.

Unit tests won't catch this. Integration tests will.

What Are We Testing?

In integration testing, we move beyond simple logic and start testing the "edges" of our application:

  1. Database Integration: Does my query actually return data from PostgreSQL?
  2. API Integration: Does my app correctly parse the JSON response from a payment gateway?
  3. File System: Can my app successfully write a PDF report to the /uploads folder?

Setting Up the Environment

Because integration tests touch real systems, they are slower and more complex than unit tests. Here is the professional workflow we use:

Never run integration tests against your "Production" or "Development" database.

  1. Create a separate test_db.
  2. Run Migrations to set up the schema.
  3. Seed the database with "dummy" data.
  4. Wipe the data after the tests finish.

Example: Testing a User Service

Let's test if our UserService can actually save a user into the database using Prisma.

import { UserService } from '../services/userService';
import { prisma } from '../lib/prisma';

describe('UserService Integration', () => {

// Clean up the database before each test
beforeEach(async () => {
await prisma.user.deleteMany();
});

test('should successfully create a user in the database', async () => {
const userService = new UserService();
const userData = { name: 'Ajay', email: 'ajay@codeharborhub.com' };

// Act: Call the service that talks to the DB
const newUser = await userService.createUser(userData);

// Assert: Check if it's in the real DB
const dbUser = await prisma.user.findUnique({
where: { email: 'ajay@codeharborhub.com' }
});

expect(dbUser).toBeDefined();
expect(dbUser.name).toBe('Ajay');
});
});

Unit vs. Integration

FeatureUnit TestingIntegration Testing
ScopeOne functionMultiple modules
DependenciesMocked (Fake)Real (DB, APIs)
SpeedMillisecondsSeconds
DebuggingEasy (Know exactly where)Harder (Could be the DB, Config, or Code)

Summary Checklist

  • I understand that integration tests check the "interaction" between modules.
  • I know that I should use a dedicated Test Database.
  • I understand that integration tests catch bugs that unit tests miss (like schema mismatches).
  • I know how to use beforeEach to keep my test database clean.
Don't Overdo It!

Because integration tests are slower, don't try to test every single "if/else" condition here. Use Unit Tests for the logic and Integration Tests just to ensure the connection works!