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