Инструменты: Jest, Mocha, встроенный assert

При работе с криптографическими операциями особенно важна проверка корректности реализации. Ошибка в тестах или их отсутствие может привести к уязвимостям, которые трудно обнаружить на этапе эксплуатации. Для JavaScript-экосистемы широко применяются три инструмента: Jest, Mocha и встроенный модуль assert. Каждый из них позволяет тестировать операции хеширования, шифрования и дешифрования, реализованные с помощью Crypto-JS.


Проверка хеширования

Хеш-функции в Crypto-JS (например, SHA-256, MD5) должны возвращать строго определённый результат для одного и того же входа.

Пример теста на корректность SHA-256:

const CryptoJS = require("crypto-js");

test("SHA-256 hashing", () => {
  const hash = CryptoJS.SHA256("hello").toString();
  expect(hash).toBe("2cf24dba5fb0a..."); // укорочено
});

Ключевые моменты:

  • сравнение выполняется с заранее известным значением (test vector)
  • результат обязательно приводится к строке методом .toString()

Использование Jest

Jest — инструмент с «из коробки» встроенными assertions и механизмами мокирования.

Установка

npm install --save-dev jest

Базовая структура теста

const CryptoJS = require("crypto-js");

describe("CryptoJS AES", () => {
  test("Encryption and decryption", () => {
    const text = "Secret message";
    const key = "password";

    const encrypted = CryptoJS.AES.encrypt(text, key).toString();
    const decrypted = CryptoJS.AES.decrypt(encrypted, key).toString(CryptoJS.enc.Utf8);

    expect(decrypted).toBe(text);
  });
});

Проверка ошибок

test("Decryption with wrong key fails", () => {
  const text = "data";
  const encrypted = CryptoJS.AES.encrypt(text, "key1").toString();
  const decrypted = CryptoJS.AES.decrypt(encrypted, "key2").toString(CryptoJS.enc.Utf8);

  expect(decrypted).not.toBe(text);
});

Использование Mocha + Chai

Mocha — гибкий тестовый фреймворк, который обычно используется вместе с библиотекой утверждений Chai.

Установка

npm install --save-dev mocha chai

Пример теста

const CryptoJS = require("crypto-js");
const { expect } = require("chai");

describe("CryptoJS Hashing", function () {
  it("should hash correctly using MD5", function () {
    const hash = CryptoJS.MD5("hello").toString();
    expect(hash).to.equal("5d41402abc4b2a76b9719d911017c592");
  });
});

Проверка AES

describe("AES encryption", function () {
  it("should encrypt and decrypt correctly", function () {
    const message = "hello";
    const key = "secret";

    const encrypted = CryptoJS.AES.encrypt(message, key).toString();
    const decrypted = CryptoJS.AES.decrypt(encrypted, key)
      .toString(CryptoJS.enc.Utf8);

    expect(decrypted).to.equal(message);
  });
});

Встроенный модуль assert

Node.js предоставляет минималистичный модуль assert, который подходит для простых проверок.

Пример

const assert = require("assert");
const CryptoJS = require("crypto-js");

const hash = CryptoJS.SHA1("test").toString();

assert.strictEqual(
  hash,
  "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3"
);

Проверка шифрования

const message = "data";
const key = "pass";

const encrypted = CryptoJS.AES.encrypt(message, key).toString();
const decrypted = CryptoJS.AES.decrypt(encrypted, key)
  .toString(CryptoJS.enc.Utf8);

assert.strictEqual(decrypted, message);

Тестирование различных кодировок

Crypto-JS активно использует кодировки, что требует отдельной проверки.

test("UTF-8 encoding", () => {
  const wordArray = CryptoJS.enc.Utf8.parse("hello");
  const result = CryptoJS.enc.Utf8.stringify(wordArray);

  expect(result).toBe("hello");
});

Ошибки в кодировке часто приводят к:

  • некорректной дешифровке
  • несовпадению хешей
  • повреждению бинарных данных

Проверка детерминированности

Хеш-функции обязаны быть детерминированными:

test("Hash is deterministic", () => {
  const input = "same input";

  const hash1 = CryptoJS.SHA256(input).toString();
  const hash2 = CryptoJS.SHA256(input).toString();

  expect(hash1).toBe(hash2);
});

Проверка случайности (для IV и salt)

Шифрование с использованием случайных параметров должно давать разные результаты:

test("Encryption produces different outputs", () => {
  const text = "secret";

  const encrypted1 = CryptoJS.AES.encrypt(text, "key").toString();
  const encrypted2 = CryptoJS.AES.encrypt(text, "key").toString();

  expect(encrypted1).not.toBe(encrypted2);
});

При этом дешифровка должна оставаться корректной.


Тестирование производительности

Хотя это не основная задача unit-тестов, иногда проверяется время выполнения:

test("Hash performance", () => {
  const start = Date.now();

  for (let i = 0; i < 1000; i++) {
    CryptoJS.SHA256("data");
  }

  const duration = Date.now() - start;
  expect(duration).toBeLessThan(500);
});

Мокирование и изоляция

При использовании Jest можно изолировать зависимости:

jest.mock("crypto-js", () => ({
  SHA256: jest.fn(() => ({
    toString: () => "mocked_hash"
  }))
}));

Это позволяет:

  • тестировать бизнес-логику отдельно от криптографии
  • ускорять выполнение тестов

Структура тестов

Рекомендуемая организация:

/tests
  hashing.test.js
  encryption.test.js
  encoding.test.js

Каждый файл покрывает отдельный аспект:

  • хеширование
  • симметричное шифрование
  • преобразование форматов

Сравнение инструментов

Характеристика Jest Mocha assert
Встроенные assertions Да Нет Да
Простота настройки Высокая Средняя Максимальная
Мокирование Да Через библиотеки Нет
Подходит для крупных проектов Да Да Ограниченно

Типичные ошибки при тестировании Crypto-JS

  • сравнение WordArray без преобразования в строку
  • игнорирование кодировки при дешифровке
  • использование случайных значений без фиксации ожидаемого результата
  • отсутствие тестов на неправильные ключи
  • проверка только «счастливого пути»

Проверка на совместимость

Иногда требуется удостовериться, что результат совпадает с другими реализациями:

test("Cross-platform hash", () => {
  const hash = CryptoJS.SHA256("hello").toString();

  expect(hash).toBe("2cf24dba5fb0a...");
});

Такие тесты особенно важны при интеграции с backend-системами.


Расширенные сценарии

Тестирование HMAC

test("HMAC SHA256", () => {
  const result = CryptoJS.HmacSHA256("message", "key").toString();

  expect(result).toBeDefined();
});

Тестирование Base64

test("Base64 encoding", () => {
  const encoded = CryptoJS.enc.Base64.stringify(
    CryptoJS.enc.Utf8.parse("hello")
  );

  const decoded = CryptoJS.enc.Base64.parse(encoded)
    .toString(CryptoJS.enc.Utf8);

  expect(decoded).toBe("hello");
});

Практика покрытия кода

Высокий уровень покрытия особенно важен для криптографии:

  • тестируются все алгоритмы
  • проверяются граничные случаи
  • учитываются некорректные входные данные

Пример:

test("Empty string hash", () => {
  const hash = CryptoJS.SHA256("").toString();

  expect(hash).toBeDefined();
});

Автоматизация тестирования

Интеграция с CI:

npm test

или для Mocha:

npx mocha

Автоматический запуск тестов позволяет:

  • быстро выявлять регрессии
  • гарантировать стабильность криптографической логики
  • контролировать изменения в библиотеке Crypto-JS