---
title: "Неизвестный тест"
type: "test-smell"
slug: "unknown-test"
url: "http://localhost:3000/ru/test-smells/unknown-test.md"
category: "Запахи утверждений"
description: "Тестовый метод, который выполняет код, но не содержит ни одной проверки, поэтому проходит, пока ничего не выбрасывает исключение, — а его подлинное назначение и то, что он проверяет, остаются неизвестными."
---
# Неизвестный тест

> Тестовый метод, который выполняет код, но не содержит ни одной проверки, поэтому проходит, пока ничего не выбрасывает исключение, — а его подлинное назначение и то, что он проверяет, остаются неизвестными.

## Signs and Symptoms

Тест, который готовит объекты и вызывает тестируемую систему, а затем **останавливается без единой проверки**. Он зелёный просто потому, что не было выброшено исключение, — а не потому, что подтверждено какое-либо ожидаемое поведение. Ничто в теле не сообщает, как выглядит «правильно».

Характерные признаки:

* Нигде в теле теста нет `expect`/`assert`/`verify`.
* «Проверка» выполняется через `console.log`/`print` результата, который человек должен оценить на глаз.
* Расплывчатое имя теста (`testChainDependencies`) и тело, не дающее ни намёка на то, что он гарантирует.
* Тест остался бы зелёным, даже если бы продакшен-метод вернул совершенно неверное значение.
* Тесты, целиком опирающиеся на «оно не упало», не говоря об этом явно.

```js
// Запах: выполняет код, печатает, ничего не проверяет — проходит независимо от того, что вернёт calculate()
test('chain dependencies', () => {
  const game = Game.newGame(0, '');
  game.setOtherGoods(Building.TOOLMAKERS, 1);
  const logic = new Logic(game);

  const res = logic.calculateChainWithDependencies(Goods.TOOLS);
  console.log(res.toString()); // нет expect(...) — что вообще здесь проверяется?
});

```

Распространённый вариант прячется за обильным мокингом: тест настраивает моки и вызывает SUT, но так и не проверяет ни возвращаемое значение, _ни_ взаимодействия с моками.

## Reasons for the Problem

**Почему это происходит**

* Был создан заготовочный/TODO-тест ("лишь бы компилировался и запускался"), а проверку так и не дописали.
* Отладочные леса — `console.log`, ручной прогон «на глазок» — попали в коммит так, будто это полноценный тест.
* При рефакторинге проверку удалили или закомментировали, но оставили подготовку данных.
* «Запускается без исключений» принимают за «работает». Дымовые тесты вполне законны, но здесь _намерение_ сделать дымовой тест нигде не выражено явно.
* Автоматически или ИИ-сгенерированные заготовки тестов, которые вызывают метод, но не проверяют результат.

**Чем это вредит**

* **Ложная уверенность.** Тест зеленеет в отчёте и добавляет покрытие строк/ветвей, но не проверяет ничего. Метрики покрытия откровенно лгут о том, насколько код защищён.
* **Нет защиты от регрессий.** Поведение может незаметно сломаться — неверное возвращаемое значение, неверное состояние — а набор тестов останется зелёным. Это худший вид теста: он требует сопровождения, но ничего не ловит.
* **Неизвестное намерение.** Читатель (или будущий сопровождающий) не может понять, какое поведение гарантируется, а значит, не может безопасно менять ни код, ни тест. Тест ничего не документирует.
* **Подрывает доверие к набору тестов.** Стоит людям заметить тесты, которые на самом деле ничего не тестируют, и они перестают верить, что зелёный значит хорошо, — а это обесценивает и все остальные тесты.

Это зеркальное отражение [рулетки проверок](#): там слишком _много_ недокументированных проверок, а в Неизвестном тесте их _нет вообще_.

## Treatment

Пусть каждый тест явно заявляет, чего он ожидает, и пусть «он просто не должен выбрасывать исключение» будет осознанным, явным выбором.

1. **Добавьте хотя бы одну проверку наблюдаемого результата** — возвращаемого значения, итогового состояния или выброшенного исключения. Замените отладочные `console.log`/print на `expect` для этого значения.
2. **Если настоящее намерение — «это не должно выбрасывать исключение», скажите об этом явно** через `expect(() => fn()).not.toThrow()` (или `await expect(fn()).resolves.toBeDefined()`). Теперь дымовой тест задокументирован, а не случаен.
3. **Для тестов, проверяющих только взаимодействия**, проверяйте коллаборатора: `expect(mock).toHaveBeenCalledWith(...)`.
4. **Удаляйте или помечайте `skip`/`todo` мёртвые заготовки** вместо того, чтобы оставлять пустой зелёный тест (`it.todo('handles chained deps')` честно фиксирует пробел, не подделывая покрытие).
5. **Включите детектор** (`jest/expect-expect`, `vitest/expect-expect` или SonarSource S2699) в CI. Если вы оборачиваете проверки в собственные вспомогательные функции, зарегистрируйте их через опцию правила `assertFunctionNames`, чтобы настоящие проверки не помечались как нарушения.

```js
// До — Неизвестный тест
test('chain dependencies', () => {
  const logic = new Logic(Game.newGame(0, ''));
  const res = logic.calculateChainWithDependencies(Goods.TOOLS);
  console.log(res.toString());
});

// После — намерение и гарантия выражены явно
test('resolves tools to the toolmakers workshop chain', () => {
  const logic = new Logic(Game.newGame(0, ''));
  const res = logic.calculateChainWithDependencies(Goods.TOOLS);
  expect(res).toHaveLength(1);
  expect(res[0].building).toBe(Building.TOOLMAKERS);
});

```

## Detected by

- **eslint-jest** `expect-expect` — jest/expect-expect (https://github.com/jest-community/eslint-plugin-jest/blob/main/docs/rules/expect-expect.md)
- **eslint-vitest** `expect-expect` — vitest/expect-expect (https://github.com/vitest-dev/eslint-plugin-vitest/blob/main/docs/rules/expect-expect.md)
- **sonar** `S2699` — Тесты должны содержать проверки (JavaScript) (https://rules.sonarsource.com/javascript/RSPEC-2699/)
- **sonar** `S2699` — Тесты должны содержать проверки (Java) (https://rules.sonarsource.com/java/RSPEC-2699/)
