---
title: "True, False, Nil, NotNil и Empty: простые проверки"
description: "Простые проверки testify: assert True False Nil NotNil Empty Zero на каждый день. Ловушки nil-интерфейса, пустого слайса и нулевых значений с примерами."
course: "Testify: тестирование Go с assert и require"
url: "https://golangmaster.ru/courses/testify/lessons/08-bool-nil-empty/"
published: "2026-09-19"
updated: "2026-09-21"
minutes: 18
---

# True, False, Nil, NotNil и Empty: простые проверки

*Курс «Testify: тестирование Go с assert и require», урок 8 из 25. ~18 минут. Обновлено 21.09.2026. Источник: https://golangmaster.ru/courses/testify/lessons/08-bool-nil-empty/*

Половина проверок в боевом тесте — простые: флаг выставлен, ошибка не вернулась, слайс не пуст, поле обнулилось. У testify на каждый такой случай есть одноимённое утверждение, и замена ручной развилки на assert.True или assert.Nil делает отчёт о провале читаемым сразу, без чтения кода теста. Разберём базовый набор, его границы и ловушки, на которых собирают баги.

После первых уроков курса в руках уже есть тяжёлая артиллерия: Equal для сравнения значений, ElementsMatch для коллекций, семейство проверок ошибок. Но повседневный тест — это не только сравнения. Проверить, что переключатель включился, что метод вернул nil вместо объекта, что очередь опустела, что счётчик обнулился — такие проверки занимают больше половины строк в тестах, которые вы будете писать каждую неделю. Testify собирает их в короткие одноимённые утверждения: True, False, Nil, NotNil, Empty, NotEmpty, Zero, NotZero, Len. Каждое работает одинаково: провал помечает тест через t.Errorf, в отчёт уходит и полученное значение, и ожидаемое, а вместе с ними — ваше сообщение, если вы его передали. В этом уроке пройдёмся по набору целиком, а затем остановимся на двух ловушках, на которых регулярно собираются редкие и жёсткие баги: nil, спрятанный в интерфейсе, и разница между пустым и нулевым значением коллекции.

### True и False: флаги без развилок

assert.True(t, ok) требует, чтобы ok был равен true, assert.False(t, ok) — чтобы false. На первый взгляд это дубль проверки ok == true через if, но разница проявляется при провале: if молча не выполнит ветку, а тест-функция с assert.True напишет в отчёт «Should be true» и покажет фактическое значение флага. Именно поэтому в тестах развилки if почти не живут: они проверяют вручную и молча, а тест обязан шуметь. Полезная деталь API: и True, и False принимают те же необязательные msgAndArgs, что и остальные утверждения — сообщение с контекстом («токен истёк за секунду до операции») попадёт в отчёт рядом с провалом. Частая ошибка новичка — assert.Equal(t, true, ok) вместо assert.True(t, ok): работает так же, но читается хуже и выводит более шумный diff. Инвертированные версии у каждой проверки тоже есть на месте: NotNil и NotEmpty — прямые антиподы Nil и Empty, и в паре с True/False они покрывают большинство сценариев с булевыми полями и результатами валидации.

### Проверка флагов валидации

```go
func TestValidation(t *testing.T) {
        user := User{Name: "Анна", Age: 30}

        assert.True(t, user.IsValid(), "заполненный пользователь должен проходить валидацию")

        user.Age = -1
        assert.False(t, user.IsValid(), "отрицательный возраст не должен проходить")
}
```

Каждая проверка снабжена сообщением с человеческим объяснением: при провале отчёт покажет не только «expected false, got true», но и причину, ради которой проверка ставилась. Сообщения — дешёвая привычка, которая экономит часы при чтении отчётов CI.

### assert.Nil: границы nil в Go

assert.Nil(t, v) проверяет, что v — нулевое значение своего типа: nil для указателей, слайсов, карт, каналов, интерфейсов и функций, ноль для чисел, пустая строка для string, false для bool. Это шире, чем проверка v == nil: для числа 0 и строки "" Nil тоже сработает, потому что в терминах testify «нулевой» и «пустой» для примитивов означают одно и то же. Не путайте с NotNil: assert.NotNil(t, err) иногда встречается в чужих тестах вместо assert.Error, но так делать не стоит — для ошибок есть специальные проверки с более ясным выводом, они разобраны в уроке про [проверку ошибок](/courses/testify/lessons/04-error-assertions/). Ещё одна привычка, которая отличает зрелый тест: проверять Nil не только на желаемые результаты, но и на то, что освобождение ресурсов обнулило указатель — так ловят использования после закрытия.

> **Nil-ловушка: интерфейс с nil-внутри не Nil.** Классическая ловушка Go: переменная интерфейсного типа, в которую положили nil-указатель конкретного типа, не равна nil. err := (*MyError)(nil); var e error = err; assert.Nil(t, e) провалится, хотя по-человечески e выглядит как nil. Внутри интерфейса живут тип и значение, и здесь пара не пустая. Это ошибка проектирования, а не testify: лечится она в коде возвращаемых типов, а не в тесте — и о ней стоит помнить, когда Nil внезапно краснеет на «очевидном» nil.

### Empty и NotEmpty: что считается пустым

assert.Empty(t, v) отвечает на вопрос «здесь ничего полезного?» для самых разных типов: пустая строка, слайс длины ноль, пустая карта, nil того же семейства, нулевой канал, ноль числа, false. Утверждение построено на helper-функции, которая сначала пробует взять длину через отражение, а для примитивов сравнивает с нулевым значением. Отсюда практическое следствие: Empty объединяет в одной проверке то, что в чистом Go требует разных выражений — len(s) == 0, s == nil, s == "". NotEmpty — зеркальная проверка, и она особенно удобна для входных данных: assert.NotEmpty(t, resp.Items, "API обязан вернуть хотя бы один элемент") читается как требование продукта, а не как код.

### Empty на четырёх типах

```go
func TestEmpty(t *testing.T) {
        var nilSlice []int
        emptySlice := []int{}
        filled := []int{1}

        assert.Empty(t, nilSlice)      // nil-слайс пуст
        assert.Empty(t, emptySlice)    // пустой слайс пуст
        assert.Empty(t, "")            // пустая строка пуста
        assert.Empty(t, map[int]int{}) // пустая карта пуста
        assert.NotEmpty(t, filled)
}
```

И nil-слайс, и слайс с нулевой длиной проходят Empty — для testify оба означают «ничего нет». Если ваш тест обязан различать эти состояния, используйте Len из урока про коллекции или явный Equal с nil.

### Что Empty считает пустым

| Тип | Пустое значение | Комментарий |
| --- | --- | --- |
| string | "" | и nil-семантики тут нет — только длина |
| слайс | nil или длина 0 | оба состояния эквивалентны |
| карта | nil или длина 0 | как и слайс |
| канал | nil | закрытый пустой канал тоже пройдёт Empty |
| число | 0 | 0, 0.0 — всё пустое |
| bool | false | false эквивалентен пустому |
| указатель | nil | а ненулевой указатель на пустую структуру — NotEmpty |

### Zero и NotZero: нулевые значения

Zero — родственник Empty, но с более строгой семантикой: он сравнивает значение с нулевым значением ТИПА, а не с абстрактной пустотой. assert.Zero(t, counter) после сброса счётчика читается точнее, чем Empty, потому что говорит о типовой нулевости. Для структур Zero проверяет рефлексией каждое поле — и здесь прячется нюанс: структура со всеми нулевыми полями пройдёт Zero, но структура с любым ненулевым полем провалит проверку с подробным diff. На практике Zero чаще всего встречается на числах и time.Duration, NotZero — на проверках, что что-то посчиталось: assert.NotZero(t, resp.Elapsed, "замер времени обязан быть ненулевым"). Для float-полей помните урок про [сравнение чисел с плавающей точкой](/courses/testify/lessons/07-in-delta/): Zero на аккумулированных долях лучше ставить только там, где ноль действительно точен.

### Какое утверждение выбрать

- Флаг или результат валидации — True и False, с сообщением о смысле проверки.
- Возможное отсутствие объекта — Nil и NotNil; для ошибок — Error и NoError, а не Nil.
- «Здесь нет данных» — Empty и NotEmpty: покрывают строки, слайсы, карты одним словом.
- Типовая нулевость после сброса — Zero и NotZero, особенно для чисел и Duration.
- Точная длина коллекции — Len: она различает nil и пустую коллекцию по контексту проверки.

> **Пустой против нулевого: цена различия в API.** Тест, который проверял NotEmpty на слайсе ответов, ловил багу сервиса, возвращавшего JSON null вместо пустого массива: клиентская библиотека превращала null в nil-слайс, NotEmpty честно проваливался, но контрактом это не лечилось. Разница «пусто, но существует» и «не существует вовсе» доходит до пользователей API: null и [] сериализуются по-разному. Решайте различие на уровне контракта и кодируйте его правильными утверждениями — Empty для «допустимо пусто», Len для «обязана быть конкретная длина», Equal с явным значением для контрактных деталей.

### Чтение провала: что покажет отчёт

Провал простой проверки информативен ровно настолько, насколько вы о нём позаботились. На Nil testify напишет ожидание nil и фактическое значение — если там спрятана структура, вывод будет многострочным. На Empty покажет тип и содержимое пустоты: строку, длину коллекции, ноль. На Zero выведет diff между нулевым значением типа и фактическим. Всё это можно и нужно снабжать сообщением: без него отчёт говорит «что», без «зачем», и через месяц после написания теста вы будете гадать, чем именно был важен этот Nil. Отдельный приём — проверять в сообщении условия, при которых проверка справедлива: «очередь пуста после Drain, но не до первого Run» — такое сообщение превращает провал в подсказку, а не в загадку.

### Сообщение как документация проверки

```go
assert.Nil(t, rows, "после Drain очередь обязана опустеть")

assert.NotEmpty(t, report.Rows,
        "отчёт без строк после обработки — сигнал пропущенного шага")
```

Оба сообщения объясняют требование, а не повторяют проверку. Первый читатель отчёта — вы сами через полгода: сообщение экономит погружение в контекст.

### Проверка: Nil и интерфейсы

**Вопрос:** В тесте объявлено: var e error = (*MyError)(nil). Что вернёт assert.Nil(t, e)?

- Пройдёт: внутри интерфейса лежит nil-указатель
- ✅ Провалит: интерфейс хранит пару (тип MyError, значение nil) и не равен nil
- Вызовет панику из-за nil-указателя
- Зависит от флагов компиляции

Это механика Go, а не testify: интерфейс равен nil, только когда равны nil и тип, и значение. Здесь тип записан, поэтому интерфейс ненулевой. Nil честно покажет провал — и это сигнал чинить возвращаемый тип в коде, а не ослаблять проверку.

### Проверка: Empty на nil и пустом

**Вопрос:** Какие проверки пройдут для var s []int (nil-слайс)?

- Только assert.Nil(t, s): Empty про nil не знает
- Только assert.Empty(t, s): Nil про слайсы не знает
- ✅ Обе: и Nil, и Empty пройдут — nil-слайс считается пустым
- Ни одна: для nil-слайса нужны специальные проверки

Nil проходит, потому что слайс действительно nil. Empty проходит, потому что testify считает пустым и nil, и коллекцию нулевой длины. Различить состояния поможет Len с ожидаемой длиной или Equal с явным nil.

### Что выведет отчёт при провале?

```go
var users []*User // nil-слайс

assert.NotEmpty(t, users, "ожидаем пользователей")
assert.Zero(t, 0)
assert.Zero(t, "0")
```

**Что выведет программа?**

- Все три проверки пройдут: nil-слайс может быть пустым, 0 и "0" — нулевые
- ✅ NotEmpty провалится, первый Zero пройдёт, второй провалится: строка "0" не нулевая
- NotEmpty и оба Zero провалятся
- NotEmpty провалится, оба Zero пройдут: строка "0" при парсинге даёт ноль

NotEmpty на nil-слайсе провалится — пользователь не появился. Zero(0) пройдёт: ноль и есть нулевое значение. Zero("0") провалится: нулевое значение строки — пустая строка, а "0" — обычная строка длины один. Zero сравнивает с нулевым значением ТИПА, а не с «наличием нуля в тексте».

### Соберите набор базовых проверок

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

```go
assert.___(t, session.IsActive, "сессия должна быть активной")

release(session)
assert.___(t, session.conn, "соединение обязано обнулиться")

assert.___(t, cache, "кэш обязан опустеть после сброса")

assert.___(t, report.Elapsed, "время обработки не может быть нулевым")
```

Подсказка: Флаг — True; обнулённый указатель — Nil; «здесь нет данных» — Empty; «что-то посчиталось» — NotZero.

Правильные ответы в пропусках: `True`, `Nil`, `Empty`, `NotZero`

### Соберите тест жизненного цикла сессии

Расставьте строки теста: создание, проверка активности, освобождение, две проверки после.

Правильный порядок строк:

1. `session := NewSession()`
2. `assert.True(t, session.IsActive)`
3. `session.Release()`
4. `assert.Nil(t, session.conn)`
5. `assert.Empty(t, session.Tokens)`
6. `assert.False(t, session.IsActive)`

### Карточки: базовые проверки

- **assert.True / assert.False** — Проверка булевых значений. Провал выводит фактическое значение и ваше сообщение — в тестах эти проверки заменяют молчаливые if.
- **assert.Nil / assert.NotNil** — Nil проверяет нулевое значение: nil указателей, слайсов, карт, каналов, интерфейсов и функций, ноль чисел, пустую строку, false. Интерфейс с nil-указателем внутри НЕ равен nil — механика Go.
- **assert.Empty / assert.NotEmpty** — «Здесь нет данных»: пустая строка, nil или длина 0 у слайсов и карт, нулевой канал, ноль числа, false. NotEmpty — удобная проверка обязательных данных ответа.
- **assert.Zero / assert.NotZero** — Сравнение с нулевым значением ТИПА: 0, "", false, все нулевые поля структуры. NotZero — «значение посчиталось». Для float — помнить про InDelta.
- **nil-слайс против пустого слайса** — Для Empty и Nil это одно и то же. Различие имеет смысл на уровне контракта: JSON null и [] сериализуются по-разному — кодируйте его через Len или Equal, если контракт важен.
- **msgAndArgs** — Необязательный хвост каждой проверки: строка-сообщение и аргументы для форматирования. Попадает в отчёт рядом с провалом — дешёвый способ объяснить смысл проверки.

### Что дальше

Базовый набор собран: флаги, nil, пустота, нулевые значения — теперь можно читать чужие тесты без словаря и писать свои так, чтобы отчёт рассказывал историю сам. Следующий шаг — сообщения: у каждого утверждения testify есть необязательный хвост msgAndArgs, и умение писать его коротко и по делу отличает тест, который приятно чинить, от теста, который просто краснеет. После сообщений перейдём к сборным конструкциям — сьютам с методами подготовки, где базовые проверки будут жить уже в организованных группах. Напомню про среду: все проверки выполняются внутри go test, поэтому полный прогон — локальный Go; в браузерной песочнице вы можете потренировать заполнение пропусков и порядок строк, а запуск тестов — уже на своей машине. Общий маршрут — на странице [курса по testify](/courses/testify/), а если хочется освежить, откуда берутся нулевые значения в Go, — это урок про [переменные и типы](/courses/go-basics/lessons/04-peremennye-konstanty/) базового курса.

## Коротко: что запомнить

- True, False, Nil, NotNil, Empty, NotEmpty, Zero, NotZero — ежедневный набор проверок testify; каждая принимает msgAndArgs.
- Nil проверяет нулевое значение типа: не только nil, но и 0, "" и false — это шире, чем v == nil.
- Интерфейс с nil-указателем конкретного типа не равен nil — ловушка Go, сигнал чинить возвращаемый тип, а не тест.
- Empty объединяет «пустоту» разных типов: nil и длина 0 для слайсов и карт, пустая строка, ноль, false.
- Zero сравнивает с нулевым значением типа: строка "0" не нулевая — провалит проверку.
- Разницу «nil против пустого» на уровне контракта кодируйте Len или Equal — JSON null и [] не одно и то же.
- Для ошибок используйте Error и NoError: Nil на err работает, но вывод менее ясный.

## Домашнее задание

Возьмите структуру с булевым полем и слайсом и напишите тест жизненного цикла: creation — True и NotEmpty, после Reset — Nil на указателе, Empty на слайсе, Zero на счётчике. К каждой проверке добавьте сообщение с объяснением смысла. Затем один раз намеренно перепутайте Empty и Zero на строковом поле и посмотрите, как отличает их вывод при провале.

## Вопросы и ответы по уроку

### Чем assert.Empty отличается от assert.Nil?

Nil — про нулевое значение конкретного значения: nil, 0, пустая строка, false. Empty — про отсутствие полезного содержимого: он проходит и для nil, и для коллекций нулевой длины. Практическое правило: Nil — для объектов и указателей, Empty — для данных внутри объектов и коллекций.

### Почему assert.Nil провалился на переменной, в которой явно лежит nil?

Скорее всего, это интерфейсная ловушка Go: в переменную интерфейсного типа записан nil-указатель конкретного типа, и пара «тип-значение» внутри интерфейса не пустая. Чинить нужно код: возвращать ненулевой тип только вместе с ненулевым значением, а в тесте — не ослаблять проверку до assert.True(t, v == nil).

### Стоит ли проверять err через assert.Nil?

Работать будет, но специальных проверок ошибок лучше: assert.NoError даёт более ясный вывод, а EqualError и ErrorIs позволяют сверить текст или саму ошибку. Nil на err оставьте там, где ошибки нет как концепции — например, в проверках обнуления ресурсов.
