Skip to content

Tests

img

References

Unit Testing

Integration Testing

System Testing (End to End Testing)

Acceptance Testing

Regression Testing

Performance Testing

Load Testing

Security Testing

What it is: Testing whether the program is safe against people who try to do bad things, like stealing data. Example: Checking whether an online store protects customers’ credit card information well.

Usability Testing

What it is: Testing whether the program is easy to use and understand. Example: Checking whether a drawing application is easy for children to understand and use.

Compatibility Testing

What it is: Testing whether the program works well on different devices and operating systems. Example: Checking whether a music application works on both iPhones and Android phones.

In short

TDD - Test Driven Development

img

Arrange, Act, Assert

img img

Functional and Non-Functional Tests

Mocks, Stubs, Fakes and Spies

Mocks

// userService.ts
import axios from "axios";

export interface User {
	id: number;
	name: string;
}

export async function getUser(userId: number): Promise<User> {
	const response = await axios.get<User>(
		`https://api.example.com/users/${userId}`
	);
	return response.data;
}
import axios from "axios";
import { getUser, User } from "./userService";

// Mocking the axios module
jest.mock("axios");
const mockedAxios = axios as jest.Mocked<typeof axios>;

describe("getUser", () => {
	it("should return user data when the API responds correctly", async () => {
		// Arrange
		const userId = 1;
		const userData: User = { id: userId, name: "John Doe" };
		mockedAxios.get.mockResolvedValue({ data: userData });

		// Act
		const result = await getUser(userId);

		// Assert
		expect(result).toEqual(userData);
		expect(mockedAxios.get).toHaveBeenCalledWith(
			`https://api.example.com/users/${userId}`
		);
	});

	it("should throw an error when the API returns an error", async () => {
		// Arrange
		mockedAxios.get.mockRejectedValue(new Error("Network Error"));

		// Act and Assert
		await expect(getUser(1)).rejects.toThrow("Network Error");
	});
});

Stubs

// emailService.ts
export interface Email {
	to: string;
	subject: string;
	body: string;
}

export async function sendEmail(email: Email): Promise<void> {
	// Imagine this function actually sends an email
	console.log(`Sending email to ${email.to}`);
}
// notificationService.ts
import { Email, sendEmail } from "./emailService";

export async function notifyUser(
	email: string,
	message: string
): Promise<void> {
	const emailData: Email = {
		to: email,
		subject: "Notification",
		body: message,
	};

	await sendEmail(emailData);
}
// notificationService.test.ts
import { notifyUser } from "./notificationService";
import * as emailService from "./emailService";

describe("notifyUser", () => {
	it("should call sendEmail with the correct parameters", async () => {
		// Arrange
		const email = "test@example.com";
		const message = "Hello, this is a notification.";

		// Stub the sendEmail function
		const sendEmailStub = jest
			.spyOn(emailService, "sendEmail")
			.mockResolvedValue();

		// Act
		await notifyUser(email, message);

		// Assert
		expect(sendEmailStub).toHaveBeenCalledWith({
			to: email,
			subject: "Notification",
			body: message,
		});

		// Cleanup
		sendEmailStub.mockRestore();
	});
});

Fakes

// databaseService.ts
export interface User {
  id: number;
  name: string;
}

export interface Database {
  getUserById(id: number): Promise<User | null>;
  saveUser(user: User): Promise<void>;
}

export class RealDatabase implements Database {
  private users: User[] = [];

  async getUserById(id: number): Promise<User | null> {
    return this.users.find(user => user.id === id) || null;
  }

  async saveUser(user: User): Promise<void> {
    this.users.push(
  }
}
// userRepository.ts
import { Database, User } from "./databaseService";

export class UserRepository {
	constructor(private database: Database) {}

	async getUser(id: number): Promise<User | null> {
		return await this.database.getUserById(id);
	}

	async addUser(user: User): Promise<void> {
		await this.database.saveUser(user);
	}
}
// userRepository.test.ts
import { UserRepository } from "./userRepository";
import { Database, User } from "./databaseService";

// Fake implementation of the Database interface
class FakeDatabase implements Database {
	private users: User[] = [];

	async getUserById(id: number): Promise<User | null> {
		return this.users.find(user => user.id === id) || null;
	}

	async saveUser(user: User): Promise<void> {
		this.users.push(user);
	}
}

describe("UserRepository", () => {
	it("should add and retrieve a user", async () => {
		// Arrange
		const fakeDatabase = new FakeDatabase();
		const userRepository = new UserRepository(fakeDatabase);
		const user: User = { id: 1, name: "John Doe" };

		// Act
		await userRepository.addUser(user);
		const retrievedUser = await userRepository.getUser(user.id);

		// Assert
		expect(retrievedUser).toEqual(user);
	});

	it("should return null if user does not exist", async () => {
		// Arrange
		const fakeDatabase = new FakeDatabase();
		const userRepository = new UserRepository(fakeDatabase);

		// Act
		const retrievedUser = await userRepository.getUser(1);

		// Assert
		expect(retrievedUser).toBeNull();
	});
});

Spies

// paymentService.ts
export interface PaymentDetails {
	amount: number;
	method: string;
}

export class PaymentService {
	async processPayment(paymentDetails: PaymentDetails): Promise<void> {
		// Imagine this function actually processes a payment
		console.log(
			`Processing payment of ${paymentDetails.amount} using ${paymentDetails.method}`
		);
	}
}
// checkoutService.ts
import { PaymentService, PaymentDetails } from "./paymentService";

export class CheckoutService {
	constructor(private paymentService: PaymentService) {}

	async checkout(paymentDetails: PaymentDetails): Promise<void> {
		await this.paymentService.processPayment(paymentDetails);
	}
}
// checkoutService.test.ts
import { CheckoutService } from "./checkoutService";
import { PaymentService, PaymentDetails } from "./paymentService";

describe("CheckoutService", () => {
	it("should call processPayment with the correct parameters", async () => {
		// Arrange
		const paymentService = new PaymentService();
		const checkoutService = new CheckoutService(paymentService);
		const paymentDetails: PaymentDetails = {
			amount: 100,
			method: "CreditCard",
		};

		// Spy on the processPayment method
		const processPaymentSpy = jest
			.spyOn(paymentService, "processPayment")
			.mockResolvedValue();

		// Act
		await checkoutService.checkout(paymentDetails);

		// Assert
		expect(processPaymentSpy).toHaveBeenCalledWith(paymentDetails);
		expect(processPaymentSpy).toHaveBeenCalledTimes(1);

		// Cleanup
		processPaymentSpy.mockRestore();
	});
});

Use Cases

Simple Summary

Practical Example