Skip to main content
Skip to main content

Testing Framework

Comprehensive testing guide for Mifty applications with built-in testing tools and best practices.

๐Ÿงช Testing Philosophyโ€‹

Mifty follows a test-driven development approach with:

  • Unit Tests - Test individual components
  • Integration Tests - Test module interactions
  • End-to-End Tests - Test complete workflows
  • API Tests - Test REST endpoints

๐Ÿš€ Quick Startโ€‹

Running Testsโ€‹

# Run all tests
npm test

# Run tests in watch mode
npm run test:watch

# Run tests with coverage
npm run test:coverage

# Run specific test file
npm test user.service.test.ts

Generated Testsโ€‹

Mifty automatically generates comprehensive tests for all your modules:

# Generate modules with tests
npm run generate

# Generated test files:
# src/modules/user/user.service.test.ts
# src/modules/user/user.controller.test.ts
# src/modules/user/user.repository.test.ts

๐Ÿ“‹ Test Structureโ€‹

Unit Testsโ€‹

// src/modules/user/user.service.test.ts
import { Test, TestingModule } from '@nestjs/testing';
import { UserService } from './user.service';
import { UserRepository } from './user.repository';

describe('UserService', () => {
let service: UserService;
let repository: UserRepository;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
UserService,
{
provide: UserRepository,
useValue: {
create: jest.fn(),
findById: jest.fn(),
update: jest.fn(),
delete: jest.fn(),
},
},
],
}).compile();

service = module.get<UserService>(UserService);
repository = module.get<UserRepository>(UserRepository);
});

describe('create', () => {
it('should create a user successfully', async () => {
const userData = {
email: 'test@example.com',
firstName: 'John',
lastName: 'Doe',
};

const expectedUser = { id: '1', ...userData };
jest.spyOn(repository, 'create').mockResolvedValue(expectedUser);

const result = await service.create(userData);

expect(repository.create).toHaveBeenCalledWith(userData);
expect(result).toEqual(expectedUser);
});
});
});

Integration Testsโ€‹

// src/modules/user/user.integration.test.ts
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import * as request from 'supertest';
import { AppModule } from '../../app.module';

describe('User Integration Tests', () => {
let app: INestApplication;

beforeEach(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();

app = moduleFixture.createNestApplication();
await app.init();
});

afterEach(async () => {
await app.close();
});

describe('/users (POST)', () => {
it('should create a new user', () => {
return request(app.getHttpServer())
.post('/users')
.send({
email: 'test@example.com',
firstName: 'John',
lastName: 'Doe',
})
.expect(201)
.expect((res) => {
expect(res.body.email).toBe('test@example.com');
expect(res.body.id).toBeDefined();
});
});
});
});

๐Ÿ”ง Test Configurationโ€‹

Jest Configurationโ€‹

// jest.config.js
module.exports = {
moduleFileExtensions: ['js', 'json', 'ts'],
rootDir: 'src',
testRegex: '.*\\.spec\\.ts$|.*\\.test\\.ts$',
transform: {
'^.+\\.(t|j)s$': 'ts-jest',
},
collectCoverageFrom: [
'**/*.(t|j)s',
'!**/*.spec.ts',
'!**/*.test.ts',
'!**/node_modules/**',
],
coverageDirectory: '../coverage',
testEnvironment: 'node',
setupFilesAfterEnv: ['<rootDir>/test/setup.ts'],
};

Test Database Setupโ€‹

// src/test/setup.ts
import { PrismaClient } from '@prisma/client';

const prisma = new PrismaClient({
datasources: {
db: {
url: process.env.DATABASE_URL_TEST,
},
},
});

beforeAll(async () => {
// Setup test database
await prisma.$connect();
});

afterAll(async () => {
// Cleanup test database
await prisma.$disconnect();
});

beforeEach(async () => {
// Clean database before each test
const tablenames = await prisma.$queryRaw<
Array<{ tablename: string }>
>`SELECT tablename FROM pg_tables WHERE schemaname='public'`;

for (const { tablename } of tablenames) {
if (tablename !== '_prisma_migrations') {
await prisma.$executeRawUnsafe(`TRUNCATE TABLE "public"."${tablename}" CASCADE;`);
}
}
});

๐ŸŽฏ Testing Best Practicesโ€‹

Test Organizationโ€‹

src/
โ”œโ”€โ”€ modules/
โ”‚ โ””โ”€โ”€ user/
โ”‚ โ”œโ”€โ”€ user.service.ts
โ”‚ โ”œโ”€โ”€ user.service.test.ts # Unit tests
โ”‚ โ”œโ”€โ”€ user.controller.ts
โ”‚ โ”œโ”€โ”€ user.controller.test.ts # Unit tests
โ”‚ โ””โ”€โ”€ user.integration.test.ts # Integration tests
โ”œโ”€โ”€ test/
โ”‚ โ”œโ”€โ”€ setup.ts # Test setup
โ”‚ โ”œโ”€โ”€ helpers/ # Test utilities
โ”‚ โ””โ”€โ”€ fixtures/ # Test data
โ””โ”€โ”€ e2e/
โ””โ”€โ”€ user.e2e-spec.ts # End-to-end tests

Test Data Managementโ€‹

// src/test/fixtures/user.fixture.ts
export const userFixture = {
validUser: {
email: 'john@example.com',
firstName: 'John',
lastName: 'Doe',
},
invalidUser: {
email: 'invalid-email',
firstName: '',
lastName: 'Doe',
},
};

// src/test/helpers/database.helper.ts
export class DatabaseHelper {
static async createUser(data = userFixture.validUser) {
return prisma.user.create({ data });
}

static async cleanUsers() {
return prisma.user.deleteMany();
}
}

๐Ÿšจ Testing Strategiesโ€‹

Test-Driven Development (TDD)โ€‹

  1. Write Test First - Define expected behavior
  2. Run Test - Ensure it fails
  3. Write Code - Implement functionality
  4. Run Test - Ensure it passes
  5. Refactor - Improve code quality

Behavior-Driven Development (BDD)โ€‹

describe('User Registration', () => {
describe('Given valid user data', () => {
describe('When creating a user', () => {
it('Then should return created user with ID', async () => {
// Test implementation
});
});
});

describe('Given invalid email', () => {
describe('When creating a user', () => {
it('Then should throw validation error', async () => {
// Test implementation
});
});
});
});

๐Ÿ“Š Test Coverageโ€‹

Coverage Reportsโ€‹

# Generate coverage report
npm run test:coverage

# View coverage in browser
open coverage/lcov-report/index.html

Coverage Targetsโ€‹

  • Statements: 90%+
  • Branches: 85%+
  • Functions: 90%+
  • Lines: 90%+

๐Ÿ” Debugging Testsโ€‹

Debug Configurationโ€‹

// .vscode/launch.json
{
"configurations": [
{
"name": "Debug Jest Tests",
"type": "node",
"request": "launch",
"program": "${workspaceFolder}/node_modules/.bin/jest",
"args": ["--runInBand", "--no-cache"],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen"
}
]
}

Debug Commandsโ€‹

# Debug specific test
npm run test:debug user.service.test.ts

# Debug with breakpoints
node --inspect-brk node_modules/.bin/jest --runInBand

๐Ÿš€ Advanced Testingโ€‹

Mocking External Servicesโ€‹

// src/test/mocks/email.service.mock.ts
export const mockEmailService = {
sendEmail: jest.fn().mockResolvedValue({ success: true }),
sendBulkEmail: jest.fn().mockResolvedValue({ success: true }),
};

// In your test
beforeEach(() => {
jest.clearAllMocks();
});

Testing Async Operationsโ€‹

describe('Async Operations', () => {
it('should handle async operations', async () => {
const promise = service.asyncOperation();

// Test pending state
expect(service.isLoading).toBe(true);

const result = await promise;

// Test completed state
expect(service.isLoading).toBe(false);
expect(result).toBeDefined();
});
});

๐Ÿ“š Resourcesโ€‹