Тестирование файловой системы

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

Модуль fs и его тестирование

Для взаимодействия с файловой системой в Node.js используется встроенный модуль fs (File System). Этот модуль предоставляет множество функций для чтения и записи файлов, работы с директориями и метаданными файлов.

Основные функции модуля:

  • fs.readFileSync и fs.readFile — чтение файлов.
  • fs.writeFileSync и fs.writeFile — запись в файл.
  • fs.existsSync — проверка существования файла или директории.
  • fs.unlinkSync и fs.unlink — удаление файлов.
  • fs.mkdirSync и fs.mkdir — создание директорий.

Тестирование взаимодействий с файловой системой требует аккуратности, чтобы избежать реальных изменений в файловой системе в процессе тестирования. Одним из способов решения этой проблемы является использование мока — замены реальных методов файловой системы на фальшивые, которые не будут изменять данные на диске.

Мокирование модуля fs

Для того чтобы избежать работы с настоящей файловой системой, можно использовать моки. Jest предоставляет инструмент jest.mock, который позволяет заменить импортируемые модули на поддельные реализации.

Пример использования мока для модуля fs:

jest.mock('fs');

const fs = require('fs');
const myModule = require('./myModule');

test('должен записывать файл', () => {
  fs.writeFileSync.mockImplementation(() => {}); // Подменяем реализацию метода

  myModule.saveFile('test.txt', 'Тестовое содержимое');
  
  expect(fs.writeFileSync).toHaveBeenCalledWith('test.txt', 'Тестовое содержимое');
});

В этом примере метод fs.writeFileSync подменяется на пустую функцию, чтобы не производить реальную запись в файл. В тесте проверяется, что метод был вызван с ожидаемыми аргументами.

Использование библиотеки mock-fs

Если тестируемые модули активно взаимодействуют с файловой системой и используются методы вроде fs.readdirSync или fs.mkdirSync, то мока самого модуля недостаточно. В таких случаях на помощь приходит библиотека mock-fs, которая предоставляет более гибкие и мощные средства для моки файловой системы.

Для начала необходимо установить библиотеку:

npm install mock-fs

Далее, используя mock-fs, можно создавать виртуальные файловые системы для тестирования:

const mockfs = require('mock-fs');
const fs = require('fs');
const myModule = require('./myModule');

beforeEach(() => {
  mockfs({
    'test.txt': 'Содержимое файла',
    'dir': {
      'file1.txt': 'Первый файл',
      'file2.txt': 'Второй файл',
    }
  });
});

afterEach(() => {
  mockfs.restore(); // Восстановление реальной файловой системы
});

test('чтение файла', () => {
  const content = fs.readFileSync('test.txt', 'utf-8');
  expect(content).toBe('Содержимое файла');
});

test('создание директории', () => {
  fs.mkdirSync('dir2');
  expect(fs.existsSync('dir2')).toBe(true);
});

В этом примере создается виртуальная файловая система с помощью mock-fs. Внутри beforeEach создаются необходимые файлы и директории. В тестах проверяется корректность работы с этой файловой системой, включая чтение файлов и создание новых директорий.

Тестирование асинхронных операций

Методы модуля fs, такие как fs.readFile, являются асинхронными и возвращают результаты через коллбэки. Тестирование асинхронных операций требует внимательности к обработке асинхронных вызовов в Jest. Для этого можно использовать async/await и done (хотя с современным Jest предпочтительнее использовать async/await).

Пример тестирования асинхронной операции с моком:

jest.mock('fs');

const fs = require('fs');
const myModule = require('./myModule');

test('асинхронное чтение файла', async () => {
  fs.readFile.mockImplementation((path, encoding, callback) => {
    callback(null, 'Содержимое файла');
  });

  const content = await myModule.readFileAsync('test.txt');
  
  expect(content).toBe('Содержимое файла');
});

В этом примере используется асинхронный метод для чтения файла. Метод fs.readFile мокается с помощью jest.mock, и тест проверяет, что возвращаемое значение соответствует ожидаемому.

Тестирование ошибок файловой системы

Особое внимание следует уделить тестированию ошибок, которые могут возникать при работе с файловой системой. Например, если файл не существует, метод fs.readFile может выбросить ошибку. В таких случаях важно корректно обрабатывать ошибки в тестах.

Пример тестирования ошибок:

jest.mock('fs');

const fs = require('fs');
const myModule = require('./myModule');

test('ошибка при чтении несуществующего файла', async () => {
  fs.readFile.mockImplementation((path, encoding, callback) => {
    callback(new Error('Файл не найден'), null);
  });

  try {
    await myModule.readFileAsync('nonexistent.txt');
  } catch (error) {
    expect(error.message).toBe('Файл не найден');
  }
});

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

Работа с потоками

Модуль fs также предоставляет функции для работы с потоками, такие как fs.createReadStream и fs.createWriteStream. Эти методы позволяют эффективно работать с большими файлами, обрабатывая их частями.

Для тестирования таких методов можно использовать моки потоков. Пример теста для чтения файла через поток:

jest.mock('fs');

const fs = require('fs');
const myModule = require('./myModule');

test('чтение файла через поток', () => {
  const mockStream = { on: jest.fn() };
  fs.createReadStream.mockReturnValue(mockStream);

  myModule.readFileStream('test.txt');

  expect(fs.createReadStream).toHaveBeenCalledWith('test.txt');
  expect(mockStream.on).toHaveBeenCalledWith('data', expect.any(Function));
});

Здесь мы подменяем поток, возвращаемый методом fs.createReadStream, и проверяем, что метод был вызван с правильными параметрами.

Заключение

Тестирование операций с файловой системой в JavaScript с использованием Jest позволяет обеспечить надежность и корректность работы программ, которые активно взаимодействуют с файлами. С помощью мокирования модуля fs или использования специализированных библиотек, таких как mock-fs, можно эффективно тестировать различные операции, включая чтение и запись файлов, обработку ошибок и асинхронные операции. Это поможет избежать ошибок в реальной файловой системе и обеспечит стабильность работы приложения.