Snapshot тестирование — метод автоматизированной проверки пользовательского интерфейса, структуры данных или результата рендеринга компонентов путём сравнения текущего состояния объекта с заранее сохранённым эталоном. В экосистеме JavaScript snapshot-подход особенно широко используется вместе с библиотеками тестирования React, Vue, Angular и Node.js-приложений.
В проектах с локализацией и интернационализацией snapshot-тесты позволяют фиксировать форматирование дат, валют, чисел и сообщений, что делает их полезным инструментом при использовании библиотеки Globalize.
Во время первого запуска теста создаётся снимок состояния — snapshot. Он сохраняется в отдельный файл. При последующих запусках результат сравнивается с эталоном.
Если данные изменились:
тест помечается как failed;
тестовый фреймворк показывает различия;
разработчик принимает решение:
Типичный жизненный цикл:
Библиотека Globalize занимается:
Все эти операции чувствительны к:
Snapshot-тесты позволяют зафиксировать корректное поведение локализации.
Пример:
const Globalize = require("globalize");
Globalize.locale("ru");
const formatter = Globalize.numberFormatter();
console.log(formatter(12345.67));
Результат:
12 345,67
Snapshot позволяет гарантировать, что:
Наиболее распространённая связка:
Установка:
npm install --save-dev jest
npm install globalize cldr-data
Globalize требует загрузки CLDR-данных.
Пример настройки:
const Globalize = require("globalize");
const cldr = require("cldr-data");
Globalize.load(
cldr.entireSupplemental(),
cldr.entireMainFor("en", "ru")
);
Globalize.locale("ru");
test("number formatting snapshot", () => {
const formatter = Globalize.numberFormatter();
expect(formatter(12345.67)).toMatchSnapshot();
});
После первого запуска создаётся файл:
exports[`number formatting snapshot 1`] = `"12 345,67"`;
Jest создаёт директорию:
__snapshots__/
Пример:
tests/
formatting.test.js
__snapshots__/
formatting.test.js.snap
describe("number formatting", () => {
test("english locale", () => {
Globalize.locale("en");
const formatter = Globalize.numberFormatter();
expect(formatter(1000.5)).toMatchSnapshot();
});
test("russian locale", () => {
Globalize.locale("ru");
const formatter = Globalize.numberFormatter();
expect(formatter(1000.5)).toMatchSnapshot();
});
});
Snapshot:
exports[`number formatting english locale 1`] = `"1,000.5"`;
exports[`number formatting russian locale 1`] = `"1 000,5"`;
test("currency formatting", () => {
Globalize.locale("ru");
const formatter = Globalize.currencyFormatter("USD");
expect(formatter(1999.99)).toMatchSnapshot();
});
Результат:
"1 999,99 $"
Форматирование дат — одна из самых критичных областей интернационализации.
test("date formatting", () => {
Globalize.locale("ru");
const formatter = Globalize.dateFormatter({
datetime: "medium"
});
const result = formatter(new Date("2025-01-10T12:30:00"));
expect(result).toMatchSnapshot();
});
Дата зависит от:
Из-за этого тесты могут быть нестабильными.
Для стабильности тестов используется:
TZ=UTC jest
Либо:
process.env.TZ = "UTC";
Globalize поддерживает plural rules.
Пример:
Globalize.loadMessages({
ru: {
cart: {
item: {
one: "товар",
few: "товара",
many: "товаров"
}
}
}
});
Тест:
test("plural forms", () => {
Globalize.locale("ru");
const pluralGenerator =
Globalize.pluralGenerator();
expect([
pluralGenerator(1),
pluralGenerator(2),
pluralGenerator(5)
]).toMatchSnapshot();
});
Globalize.loadMessages({
ru: {
hello: "Привет, {name}"
}
});
test("message formatting", () => {
Globalize.locale("ru");
const formatter =
Globalize.messageFormatter("hello");
expect(
formatter({ name: "Иван" })
).toMatchSnapshot();
});
Jest поддерживает inline snapshots.
test("inline snapshot", () => {
Globalize.locale("ru");
const formatter = Globalize.numberFormatter();
expect(formatter(5000)).toMatchInlineSnapshot(
`"5 000"`
);
});
Преимущества:
Недостатки:
Команда:
jest -u
или:
npm test -- -u
Jest перезапишет snapshots.
Опасные ситуации:
Snapshot нельзя обновлять автоматически без анализа diff.
Snapshot-тесты активно применяются в CI.
Типичный pipeline:
Lint
↓
Unit tests
↓
Snapshot tests
↓
Build
При изменении локализации CI сразу обнаруживает проблему.
const locales = ["en", "ru", "de", "fr"];
describe.each(locales)(
"locale %s",
locale => {
test("number formatting", () => {
Globalize.locale(locale);
const formatter =
Globalize.numberFormatter();
expect(
formatter(123456.78)
).toMatchSnapshot();
});
}
);
Globalize часто используется внутри React-приложений.
function Price({ value }) {
const formatter =
Globalize.currencyFormatter("USD");
return <span>{formatter(value)}</span>;
}
Тест:
import renderer from "react-test-renderer";
test("Price component", () => {
const tree = renderer
.create(<Price value={100} />)
.toJSON();
expect(tree).toMatchSnapshot();
});
Нестабильные данные делают snapshot бесполезным.
Проблемные значения:
jest.spyOn(Date, "now")
.mockReturnValue(1000000);
jest.spyOn(Math, "random")
.mockReturnValue(0.5);
Иногда объект слишком сложный.
Пример:
expect(user).toMatchSnapshot();
Snapshot может содержать:
Лучше сериализовать данные вручную.
const normalized = {
name: user.name,
locale: user.locale
};
expect(normalized).toMatchSnapshot();
Globalize нередко участвует в генерации HTML-шаблонов.
const html = `
<div>
${formatter(1000)}
</div>
`;
expect(html).toMatchSnapshot();
Server Side Rendering особенно чувствителен к локализации.
Проблемы:
Snapshot помогает фиксировать результат SSR.
Обновление CLDR способно изменить:
После обновления snapshots часто меняются массово.
Пример diff:
- "1,000.50"
+ "1 000,50"
Такой diff может означать:
Плохая практика:
expect(hugeObject).toMatchSnapshot();
Проблемы:
Качественный snapshot:
Snapshot не заменяет unit-тесты.
Плохой пример:
expect(result).toMatchSnapshot();
Без явных проверок невозможно понять причину ошибки.
Лучший подход:
expect(result).toBe("1 000,50");
expect(result).toMatchSnapshot();
Jest поддерживает сериализаторы.
expect.addSnapshotSerializer({
test: value => typeof value === "string",
print: value => value.trim()
});
Это полезно для:
Globalize.loadMessages({
ru: {
items: {
one: "{count} товар",
few: "{count} товара",
many: "{count} товаров"
}
}
});
Тест:
test("plural messages", () => {
Globalize.locale("ru");
const formatter =
Globalize.messageFormatter("items");
const values = [
formatter({ count: 1 }),
formatter({ count: 2 }),
formatter({ count: 10 })
];
expect(values).toMatchSnapshot();
});
Snapshot отлично обнаруживает:
Локализация активно использует Unicode.
Пример:
1 000
Между числами используется non-breaking space.
Визуально:
1 000
может выглядеть одинаково, но snapshot обнаружит различие.
Иногда тесты ломаются из-за:
U+00A0U+202FДля диагностики:
console.log(
Array.from(value).map(
ch => ch.charCodeAt(0)
)
);
При большом количестве locale:
Лучше:
number-formatting.test.js
currency-formatting.test.js
plural.test.js
чем:
globalize-all.test.js
Важно проверять:
test("negative currency", () => {
Globalize.locale("en");
const formatter =
Globalize.currencyFormatter("USD");
expect(
formatter(-500)
).toMatchSnapshot();
});
Слишком большие snapshots:
Snapshot плохо подходит для:
Наиболее эффективные сценарии:
describe("Globalize localization", () => {
beforeAll(() => {
Globalize.loadMessages({
ru: {
hello: "Привет"
}
});
});
test("currency", () => {
Globalize.locale("ru");
const formatter =
Globalize.currencyFormatter("RUB");
expect(
formatter(1500)
).toMatchSnapshot();
});
test("date", () => {
Globalize.locale("ru");
const formatter =
Globalize.dateFormatter({
datetime: "short"
});
expect(
formatter(
new Date("2025-01-01")
)
).toMatchSnapshot();
});
test("message", () => {
Globalize.locale("ru");
const formatter =
Globalize.messageFormatter("hello");
expect(
formatter()
).toMatchSnapshot();
});
});
Только необходимые данные:
expect({
amount,
locale
}).toMatchSnapshot();
delete result.timestamp;
new Date("2025-01-01T00:00:00Z")
beforeEach(() => {
Globalize.locale("ru");
});
TZ=UTC
Автоматическое обновление snapshots без анализа — источник скрытых ошибок.
expect(apiResponse).toMatchSnapshot();
expect({
id: Math.random()
}).toMatchSnapshot();
expect(allLocales).toMatchSnapshot();
expect(result).toMatchSnapshot();
без:
expect(result).toContain("$");
Крупные проекты обычно строят структуру:
tests/
snapshots/
formatting/
messages/
currencies/
plural/
При code review анализируют:
Для Globalize snapshot-тестирование особенно важно, поскольку библиотека работает с огромным количеством региональных правил. Даже незначительное изменение конфигурации может привести к массовым визуальным ошибкам:
Snapshot-тесты позволяют обнаруживать подобные проблемы автоматически ещё до попадания изменений в production.