Обычный пятничный вечер. Ты уже расслабился, попивая кружечку чая или чего покрепче... Но тут, как назло, бомбит личку в телеге твой надоедливый коллега DevOps со скринами ошибок твоего кривого коммита на серваке. Спустя четно потраченные нервы и логирование всего и вся, все же удалось найти ту самую строчку кода, что так мешала всем жить. Не хотелось бы попадать часто в такие ситуации, да и обременять последующее поколение на те же муки. В этой статье мы рассмотрим различные методики написания одного и того же теста, их плюсы и минусы, а также попробуем определить: какого плана стоит придерживаться в будущем. Не будем тянуть кота за все подробности и давайте разбираться как наиболее эффективно и элегантно проверять ваш код на возможные уловки компилятора или нарушения здравого смысла. Предположим, что ваш стартап не удался, ваш пет проект онлайн магазина (на код коего благородный мастер с ютуба наделил вас единственными и исключительными правами) не шибко привлекает рекрутеров. Да и сидеть за монитором целыми днями - только зрение садить. Самое время переквалифицироваться и пойти потаксовать по улицам родного города. Напишем простенькую программу, в которой таксист может ехать, выбирать разные машины в зависимости от времени суток (мало ли жена решила нам подсобить с семейным заработком) и отправлять отчет в таксопарк в формате JSON.  Quick notes: Для простоты эксперимента мы не отправляем в отчет данные о машине Vehicle т.к. это интерфейс и его так просто не замаршаллить, а придумывать способ как это сделать нас пока не касается.  Что если здесь появятся приватные поля в структурах? До тех пор, пока мы не зависим от структур с другого пакета, нам бояться нечего. В противном же, пришлось бы такие поля экспортировать или приписывать методы для получения таковых. Имхо, лучше объявлять поля публичными, пока нет веских оснований делать их недосягаемыми. Ну и нафига я джаву учил тогда?  Мы таксисты гордые и ездим Comfort+ Для начала напоминание даже для самых закаленных в боях гоферов: A unit test is a test of behaviour whose success or failure is wholly determined by the correctness of the test and the correctness of the unit under test. - Kevlin Henney И немного отсебятины от автора: Тесты в первую очередь пишутся для других разработчиков и должны полностью отображать возможности и исходы тестируемой сущности. Ну-с, приступим: Такой стиль предложил использовать Билл Кеннеди. Здесь приводится доходчивое описание и разделение проверок на логические компоненты.  (8-10) Инициализируем параметры, конфиги и тд, являющиеся общими для всего теста (12) С помощью логов создаем детальное описание того, что будет проверять наш тест. Это необходимая часть, т.к. тестируемая сущность может быть намного сложнее и иметь множество разных применений и отдельных тестов для этого. Всегда начинаем с конструкции "Given the need to ..." (14) Логически разделяем тесты с testID (15) Объявляем один из наших подтестов. Обратите внимание на табуляцию и структуру сообщения. Всегда начинаем с ID теста и конструкции "When ...". Обособление тела подтеста кавычками полезно не только для читабельности, но и для изолирования от других, что, к примеру, позволит нам объявлять переменные с теми же именами Таким образом, даже несмотря на саму реализацию логики таксиста, благодаря логам уже понятно: что и когда будет тестироваться.  Здесь мы смотрим: правильную ли машину нам присвоили при вызове метода SetVehicle. ок должен вернуть нам true или false, но как это проверить? Рассмотрим несколько вариантов. Недостатками такого очевидного способа являются: Аж 3 использованные строчки кода Не всеобъемлющее описание проверки.  В общем, заносим данный подход смело в инвентарь плохих практик. В логах это выглядит примерно так: Вывод в логах, конечно, мое почтение... Однако, даже у такого 'crazy' способа есть ряд недостатков: Излишнее повторение кода Запоминание табуляции Маркеры. Скажем, для нашего странного коллеги, пользующимся командной строкой Windows или чем либо еще неординарным, такие финты ушами не пройдут, т.к. вместо галочек и крестиков будут виднеться непонятные символы Вывод желаемого значения при ошибке не всегда читабелен. Что если бы мы сравнивали большие числа или очень длинные имена? К примеру: "Should be able to get 925120518250 : 925120158250". Ну как, сразу ли нашли где не сходится?  Время, потраченное на оформление теста Как бы грустно это не было, но Билл отправляется в инвентарь (но не с концами). Нам понадобится знаменитый и очень удобный пакет https://github.com/stretchr/testify, а также немного педантичности от Билла в оформлении сообщения: require - пакет, позволяющий проверять параметр на определенное значение, а в противном случае тут же прекращает тест. Возможно, у вас больше на слуху пакет assert. Различие в том, что он не сразу останавливает тест. А поскольку в 90% случаев нам нет смысла совершать дальнейшие проверки после ошибки, то лучше использовать его только в Table Driven тестах.  Преимущества данного метода: Лаконичность Читабельность. В отличие от вызова t.Fatalf, вызов нашей функции уже дает нам понятие о том, чего ожидает проверка от параметра Детальное описание проверки с помощью конструкции "Should ..." Более чем детальный вывод ошибки Раз уж мы нашли оптимальный для нас подход, продолжим наш тест в том же духе: Единственный момент, который стоит уточнить, это вызов метода Diff из пакета https://github.com/google/go-cmp. Это гугловский пакет, позволяющий сравнивать структуры между собой. Быстрее и эффективнее, чем более известный способ через reflect.DeepEqual.  В пакете testify тоже есть похожая и часто используемая функция Equal. Единственная причина по которой мы используем Diff вместо Equal: возможность исключить из проверки некоторые поля. Здесь мы не можем гарантировать одинаковое время создания отчета, поэтому можем скипнуть это поле. Ну и следующий тест будет аналогичен первому, так что подведем на этом итог. Уделяйте больше внимания тестам, это всегда окупится. Избегайте проверок без сопутствующего описания. И главное: заботьтесь о том, кто будет читать ваш код и с ним в дальнейшем работать. Почта: duman070601@gmail.com LinkedIn  Golang backend developer