JavaRush /Курсы /Модуль 2: Fullstack /Моки и заглушки в тестах

Моки и заглушки в тестах

Модуль 2: Fullstack
9 уровень , 3 лекция
Открыта

4.1 Продвинутое тестирование

Моки и заглушки (mock and stubs) — это важные инструменты в арсенале тестировщика, которые позволяют изолировать тестируемый код и управлять его окружением. Они помогают симулировать поведение зависимостей, таких как функции, модули или объекты, и контролировать их взаимодействие с тестируемым кодом.

Сейчас вы изучите, что такое моки и заглушки, как их использовать в юнит-тестах с TypeScript и Jest, а мы приведем примеры использования.

Что такое моки и заглушки?

  1. Мок (mock) — это объект, который имитирует поведение реального объекта или модуля. Моки используются для тестирования взаимодействия между объектами и проверки вызовов методов и их параметров.
  2. Заглушка (stub) — это объект, который заменяет реальную зависимость в тесте и возвращает заранее определенные значения. Заглушки используются для контроля над зависимостями и определения поведения в тестах.

Зачем использовать моки и заглушки?

  1. Изоляция тестов: моки и заглушки помогают изолировать тестируемый код от его зависимостей, что упрощает написание тестов и повышает их надежность.
  2. Контроль над окружением: моки и заглушки позволяют точно контролировать окружение теста и задавать нужное поведение зависимостей.
  3. Тестирование крайних случаев: с помощью моков и заглушек можно легко тестировать крайние случаи и сценарии, которые сложно воспроизвести в реальной среде.
  4. Ускорение тестов: использование моков и заглушек вместо реальных зависимостей позволяет значительно ускорить выполнение тестов, так как исключаются задержки, связанные с внешними системами (например, базами данных или сетевыми запросами).

4.2 Использование моков и заглушек с Jest

Jest предоставляет мощные встроенные функции для создания моков и заглушек, что делает его идеальным выбором для тестирования с использованием этих инструментов.

Рассмотрим пример, где мы используем заглушку для замены функции, которая делает сетевой запрос.

1. Создание функции для тестирования

Создадим функцию fetchData в файле src/api.ts, которая делает HTTP-запрос:

TypeScript
    
      import axios from 'axios';

      export async function fetchData(url: string): Promise<string> {
        const response = await axios.get(url);
        return response.data;
      }
    
  

2. Создание заглушки для функции

Теперь создадим тест для этой функции в файле tests/api.test.ts. Мы будем использовать Jest для создания заглушки на функцию axios.get:

TypeScript
    
      import axios from 'axios';
      import { fetchData } from '../src/api';

      jest.mock('axios');
      const mockedAxios = axios as jest.Mocked<typeof axios>;

      test('fetches data successfully from an API', async () => {
        const data = 'peanut butter';
        mockedAxios.get.mockResolvedValue({ data });

        const result = await fetchData('https://example.com');
        expect(result).toBe(data);
      });
    
  

Здесь мы используем jest.mock для замены модуля axios и создаем заглушку на метод get, которая возвращает заранее определенные данные.

4.3 Пример использования моков

Рассмотрим пример, где мы используем моки для проверки взаимодействия между объектами.

1. Создание классов для тестирования

Создадим два класса в файле src/userService.ts: UserService и UserRepository. UserService использует UserRepository для получения данных о пользователях:

TypeScript
    
      export class UserRepository {
        getUserById(id: number): string {
          // Имитация обращения к базе данных
          return `User ${id}`;
        }
      }

      export class UserService {
        constructor(private userRepository: UserRepository) {}

        getUser(id: number): string {
          return this.userRepository.getUserById(id);
        }
      }
    
  

2. Создание моков для классов

Теперь создадим тест для класса UserService в файле tests/userService.test.ts. Мы будем использовать Jest для создания мока на класс UserRepository:

TypeScript
    
      import { UserRepository, UserService } from '../src/userService';

      test('UserService calls UserRepository.getUserById', () => {
        const userRepository = new UserRepository();
        const userRepositoryMock = jest.spyOn(userRepository, 'getUserById');
        userRepositoryMock.mockReturnValue('User 1');

        const userService = new UserService(userRepository);
        const user = userService.getUser(1);

        expect(userRepositoryMock).toHaveBeenCalledWith(1);
        expect(user).toBe('User 1');
      });
    
  

Здесь мы используем jest.spyOn для создания мока на метод getUserById и проверяем, что он был вызван с правильным параметром.

4.4 Использование мок-функций

Jest также предоставляет возможность создавать мок-функции, которые могут быть полезны для замены небольших зависимостей или проверки вызовов функций.

Пример мок-функции

Создадим простой пример, где мы используем мок-функцию для замены зависимости:

TypeScript
    
      export function doSomething(callback: (message: string) => void): void {
        callback('Hello, World!');
      }
    
  

Теперь создадим тест для этой функции в файле tests/doSomething.test.ts:

TypeScript
    
      import { doSomething } from '../src/doSomething';

      test('calls the callback with "Hello, World!"', () => {
        const mockCallback = jest.fn();

        doSomething(mockCallback);

        expect(mockCallback).toHaveBeenCalledWith('Hello, World!');
      });
    
  

В этом примере мы создаем мок-функцию jest.fn() и проверяем, что она была вызвана с правильным аргументом.

4.5 Расширенные примеры использования моков и заглушек

1. Тестирование асинхронных функций с моками

Рассмотрим пример, где мы используем моки для тестирования асинхронной функции:

TypeScript
    
      import { fetchData } from '../src/api';
      import axios from 'axios';

      jest.mock('axios');
      const mockedAxios = axios as jest.Mocked<typeof axios>;

      test('fetches data successfully from an API', async () => {
        const data = { data: 'peanut butter' };
        mockedAxios.get.mockResolvedValue(data);

        const result = await fetchData('https://example.com');
        expect(result).toBe('peanut butter');
      });

      test('fetches erroneously from an API', async () => {
        const errorMessage = 'Network Error';
        mockedAxios.get.mockRejectedValue(new Error(errorMessage));

        await expect(fetchData('https://example.com')).rejects.toThrow(errorMessage);
      });
    
  

В этом примере мы тестируем как успешный случай, так и ошибочный случай с использованием моков.

2. Использование глобальных моков

Иногда нужно создать глобальные моки для использования в нескольких тестах. Это можно сделать с помощью конфигурации Jest.

Создайте файл tests/setupTests.ts:

TypeScript
    
      import axios from 'axios';

      jest.mock('axios');
    
  

Обновите конфигурацию Jest jest.config.js, чтобы включить этот файл:

JavaScript
    
      module.exports = {
        preset: 'ts-jest',
        testEnvironment: 'node',
        setupFilesAfterEnv: ['./tests/setupTests.ts'],
        testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'],
      };
    
  

Теперь модули будут замокированы для всех тестов, и вы сможете использовать моки в каждом тесте.

3
Задача
Модуль 2: Fullstack, 9 уровень, 3 лекция
Недоступна
Заглушка HTTP-запроса
Заглушка HTTP-запроса
3
Задача
Модуль 2: Fullstack, 9 уровень, 3 лекция
Недоступна
Проверка вызова методов
Проверка вызова методов
3
Задача
Модуль 2: Fullstack, 9 уровень, 3 лекция
Недоступна
Мок-функция для колбека
Мок-функция для колбека
3
Задача
Модуль 2: Fullstack, 9 уровень, 3 лекция
Недоступна
Мок асинхронного запроса
Мок асинхронного запроса
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ