Какое именование файлов тестов принято в golang
Перейти к содержимому

Какое именование файлов тестов принято в golang

  • автор:

Golang

Имена в Go так же важны, как и в любом другом языке. Они даже имеют семантический эффект: видимость имени вне пакета определяется тем, написан ли первый символ в верхнем регистре. Поэтому стоит потратить немного времени на ознакомление с соглашениями об именах в Go программах.

Имена пакетов

Когда пакет импортируется, имя пакета становится средством доступа для его содержимого. После

import "bytes"

импортирующий пакет может говорить о bytes.Buffer . Это полезно, если каждый, кто использует пакет, может использовать одно и то же имя для ссылки на его содержимое, которое подразумевает, что имя пакета должно быть хорошим: коротким, точным, запоминающимся. По договоренности, пакетам даются имена из строчных букв, из одного слова; не должно быть необходимости подчеркивания или mixedCaps. Err — кратко, так как все использующие ваш пакет будут печатать это имя. И не беспокойтесь априори о коллизиях имен. Имя пакета является только именем по умолчанию для импорта; оно не должно быть уникальным по всему исходному коду, и в редком случае коллизии импортирующий пакет может выбрать другое имя для локального использования. В любом случае путаница встречается редко, потому что имя файла в импорте определяет, какой пакет используется.

Другое соглашение заключается в том, что имя пакета является базовым именем его исходного каталога; пакет в src/encoding/base64 импортируется как «encoding/base64» , но имеет имя base64 , не encoding_base64 и не encodingBase64 .

Импортер пакета будет использовать имя для ссылки на его содержимое, поэтому экспортированные имена в пакете могут использовать этот факт, чтобы избежать проблем. (Не используйте нотацию import. , которая может упростить тесты, которые должны выполняться вне пакета, который они тестируют, но в противном случае их следует избегать.) Например, buffered reader type в пакете bufio называется Reader , не BufReader , потому что пользователи видят его как bufio.Reader , что является ясным, кратким именем. Более того, потому что импортированные сущности всегда адресуются с именем своего пакета, bufio.Reader не конфликтует с io.Reader . Аналогично, функция для создания новых экземпляров ring.Ring — которая является определением конструктора в Go — обычно называется NewRing , но так как Ring является единственным типом, экспортируемым пакетом, и поскольку пакет называется ring , он называется просто New , что клиенты пакета видят как ring.New . Используйте структуру пакета для помощи в выборе хороших имен.

Еще один короткий пример — Once.Do ; Once.Do(setup) хорошо читается и не улучшится от записи как Once.DoOrWaitUntilDone(setup) . Длинные имена автоматически не делают вещи более читабельными. Полезный комментарий к документу часто может быть более ценным, чем дополнительное длинное имя.

Getters

Go не обеспечивает автоматическую поддержку геттеров и сеттеров. Нет ничего плохого в том, чтобы самим предоставлять геттеры и сеттеры, и это часто уместно, но это ни идиоматично, ни необходимо помещать Get в имя геттера. Если у вас есть поле с именем owner (нижний регистр, не экспортируется), метод получения должен называться Owner (верхний регистр, экспортируется), а не GetOwner . Использование имен в верхнем регистре для экспорта предоставляет возможность различать поле от метода. Функция сеттер, если необходимо, скорее всего, будет называться SetOwner . Оба имени хорошо читаются на практике:

owner := obj.Owner() if owner != user

Имена интерфейсов

По соглашению интерфейсы с одним методом называются именем метода плюс суффикс -er или аналогичная модификация для создания существительного агента: Reader , Writer , Formatter , CloseNotifier и т. д.

Есть несколько таких имен, и это полезно, использовать их и имена функций, которые они захватывают. Read , Write , Close , Flush , String и подобные канонические подписи и значения. Чтобы избежать путаницы, не называйте свой метод одним из этих имен, если только он не имеет такую ​​же подпись и значение. И наоборот, если ваш тип реализует метод с тем же значением, что и метод известного типа, дайте ему то же имя и подпись; вызывайте метод преобразования строк String , а не ToString .

MixedCaps

Наконец, в Go принято использовать MixedCaps или mixedCaps вместо подчеркивания для записи имени из нескольких слов.

  • Эффективный Go: комментарии
  • Эффективный Go: форматирование
  • Эффективный Go: введение

Быстрый старт GoLang. Часть 1: Лучшие практики в Go

Лучшие практики в Go

GoLang

Ключевые особенности и лучшие практики в Golang. Комментарии и авто генерация документации.

Быстрый старт GoLang — это краткий курс программирования на языке Go для начинающих. В первой главе мы рассмотрим ключевые особенности языка и лучшие практики в Go для комфортного и качественного программирования.

Язык программирования Go (Golang) разработан с учетом опыта создания системных языков программирования, а также с целью устранения сложностей, связанных с современными языками. Давайте рассмотрим его ключевые особенности…

Ключевые особенности

  1. Статическая типизация: Go использует статическую типизацию, что означает, что типы переменных определяются на этапе компиляции. Это способствует безопасности типов и облегчает обнаружение ошибок.
  2. Компиляция: Go компилируется в машинный код, что обеспечивает высокую производительность исполняемых файлов и упрощает развертывание программ.
  3. Сборка мусора: Go имеет встроенный сборщик мусора, который автоматически управляет выделением и освобождением памяти, снижая риск утечек памяти и ошибок.
  4. Простота синтаксиса: Go имеет лаконичный и простой синтаксис, что облегчает чтение и написание кода. Это также способствует быстрому обучению языку.
  5. Горутины и каналы: Go вводит горутины и каналы для упрощения параллелизма и конкуренции в программах. Горутины — это легковесные потоки исполнения, а каналы — это средства для безопасного обмена данными между горутинами.
  6. Интерфейсы: Go использует интерфейсы для абстракции поведения и обеспечения гибкости в коде. Интерфейсы в Go неявны, что означает, что структура, имеющая определенные методы, автоматически удовлетворяет интерфейсу.
  7. Поддержка Unicode: Go имеет встроенную поддержку Unicode, что позволяет обрабатывать текст на разных языках и символах без дополнительных усилий.
  8. Стандартная библиотека: Go имеет обширную стандартную библиотеку, предоставляющую функциональность для различных задач, таких как работа с файлами, сетями, строками, математическими операциями и многим другим.
  9. Инструментарий: Go включает в себя инструменты для управления пакетами, форматирования кода, генерации документации, тестирования и профилирования. Это упрощает разработку, отладку и поддержку кода на Go.
  10. Кросс-платформенность: Go поддерживает кросс-компиляцию, что позволяет разработчикам создавать исполняемые файлы для разных операционных систем (Windows, macOS, Linux) и архитектур (x86, x64, ARM) из одного и того же исходного кода.
  11. Сообщество и экосистема: Go имеет активное и поддерживающее сообщество разработчиков, которое создает и поддерживает множество сторонних библиотек и инструментов. Это расширяет возможности языка и предоставляет разработчикам больше инструментов для решения разнообразных задач.
  12. Оптимизация для современных архитектур: Go спроектирован с учетом современных многоядерных процессоров и сетевых систем, что позволяет эффективно использовать ресурсы и достигать высокой производительности.

Особенности языка Go делают его привлекательным выбором для разработки высокопроизводительных системных и сетевых приложений, а также для решения множества других задач, связанных с программированием.

Лучшие практики в Go и методы разработки

При разработке на языке GoLang, следование лучшим практикам и методам поможет вам создавать более стабильные, эффективные и поддерживаемые приложения. Из них хотелось бы выделить:

  1. Форматирование кода: Используйте инструмент ‘ gofmt ‘ для автоматического форматирования вашего кода. Это поможет сохранить единообразие стиля и улучшит читаемость кода.
  2. Структурирование кода: Разделите ваш код на пакеты и модули, которые имеют четкую ответственность и предоставляют хорошо определенные интерфейсы.
  3. Комментирование кода: Пишите информативные комментарии к вашему коду, особенно к экспортируемым функциям и типам. Используйте комментарии в стиле ‘ Godoc ‘ для автоматической генерации документации.
  4. Обработка ошибок: Вместо игнорирования ошибок, обрабатывайте их явно, используя идиоматический подход Go с возвращением ошибок как значений. Это делает ваш код более надежным и устойчивым к ошибкам.
  5. Использование интерфейсов: Используйте интерфейсы для определения абстракций и зависимостей между компонентами вашего приложения. Это упрощает тестирование и содействует гибкости кода.
  6. Тестирование: Создавайте модульные тесты для проверки поведения вашего кода. Используйте встроенный пакет ‘ testing ‘ и инструмент ‘ go test ‘ для автоматизации тестирования.
  7. Конкурентность: Используйте горутины и каналы для эффективной и простой реализации параллелизма и конкуренции в вашем коде. Избегайте использования общего состояния и блокировок, если возможно.
  8. Использование стандартной библиотеки: Стандартная библиотека Go предоставляет множество функций для решения общих задач. Используйте стандартную библиотеку, когда это возможно, чтобы снизить зависимость от сторонних пакетов.
  9. Профилирование и оптимизация: Используйте инструменты профилирования, такие как ‘ pprof ‘, чтобы определить узкие места в вашем коде и оптимизировать его для лучшей производительности. Однако не забывайте о принципе «преждевременная оптимизация — корень всех зол». Сначала сосредоточьтесь на чистоте и стабильности кода, а затем проводите оптимизацию при необходимости.
  10. Управление зависимостями: Используйте ‘ Go Modules ‘ для управления зависимостями вашего проекта. Это позволяет гарантировать совместимость версий пакетов и упрощает развертывание вашего приложения.
  11. Непрерывная интеграция и развертывание: Настраивайте непрерывную интеграцию и развертывание (CI/CD) для автоматической сборки, тестирования и развертывания вашего приложения при изменении кода. Это повышает стабильность и снижает вероятность появления ошибок в продакшене.
  12. Безопасность: Обращайте внимание на безопасность вашего приложения, особенно при работе с внешними данными, сетью и криптографией. Следите за обновлениями и исправлениями в зависимостях вашего проекта.
  13. Переносимость кода: Стремитесь к переносимости вашего кода между различными платформами и архитектурами, насколько это возможно. Это делает ваше приложение более гибким и обеспечивает легкость развертывания на разных системах.

Форматирование кода с использованием инструмента ‘gofmt’ для автоматического форматирования

‘ gofmt ‘ — это инструмент командной строки, входящий в состав инструментария Go, который автоматически форматирует исходный код Go в соответствии с официальными рекомендациями по стилю кодирования. Использование ‘ gofmt ‘ обеспечивает единообразие стиля кодирования в вашем проекте, улучшает читаемость и упрощает доработку Вашего кода.

Вот как использовать ‘ gofmt ‘ для форматирования вашего кода:

  • Форматирование одного файла: Чтобы отформатировать один файл Go, выполните следующую команду:
gofmt -w yourfile.go Code language: Go (go)

‘ -w ‘ указывает ‘ gofmt ‘ перезаписать файл с отформатированным кодом.

  • Форматирование всех файлов в каталоге: Чтобы отформатировать все файлы Go в каталоге и его подкаталогах, выполните следующую команду:
gofmt -w . 
Точка '.' указывает на текущий каталог.

Концепция написания unit-tests в golang

Тесты. Как много в этом слове. Кажется, что в этом слове вместилось столько боли, связанной с потраченным временем. Но на самом деле тесты — это еще больше спасенного времени, да нервных клеток заодно. Ведь что такое тесты? Тест — это имитация пользователя, который заходит на страницу. И если ошибку найдут тесты, то её уже не увидит пользователь, а это значит, что доверия к сайту будет больше. А доверие к сайту — это довольный работодатель и его хорошее отношение к своим сотрудникам.

Да писать тесты это долго, нудно и порой больно. Как человек, которому на протяжении полугода сбрасывают написание практически всех тестов, могу с некоторой ответственностью заявить, что порой написание тестов едва ли не сложнее, чем написание самого кода. Ведь при написании кода у тебя есть определённая цель, видение своего кода.

А вот при написании тестов ты сталкиваешься с многими подводными камнями: сценарии, данные, проверка того, что пришло на страницу. Подсознательно хочется проверить все и сразу, и не знаю, как другие люди, но порой я от наплыва мыслей и идей терялся, не зная, какой сценарий писать.

Автоматизация тестирования во Vue приложении

Как настроить интеграционные тесты во Vue приложении?

Итак, первая концепция, которую я разработал для себя и которой я тщательно следую при написании тестов — это самый детальный подход к планированию работы и тщательнейший тайм-менеджмент.

Итак, планирование работы. Не надо садиться писать тест сразу же после того, как ты добавил новую строчку кода. Порой лучше закончить со своей работой, а потом, еще раз пройдясь по ней взглядом, ты замечаешь то, что упустил прежде, и вносишь изменения в свой код. Тесты следует перерабатывать с учетом новых данных. И поэтому лучше сначала убедиться, что твой код закончен, и только после этого приступать к написанию тестов. Только так ты можешь избежать потерь драгоценного времени, ведь только так ты можешь видеть цельный код, видеть, как должна работать страница, и это помогает продумать сценарии того, что может пойти не так.

Теперь вторая часть концепции — время. Задач всегда много, и порой времени не хватает даже на них, не говоря уже о написании тестов, и чаще всего отсутствие тестов аргументируется нехваткой времени.

Нет, ребят. Так это не работает. Тесты — это важная часть кода. Это твой щит, твоя подушка безопасности. Либо ты отловишь багу, нажав кнопку прогона тестов, и решишь её на месте, либо тебе вернут твой код со словами переделать, и тем самым ты подорвешь к себе доверие. Тщательно планируй свое время, чтобы уделить должное внимание и написанию тестов.

Пускай ты не сможешь протестировать все сразу, но даже начав, ты уже заложишь фундамент. Каждый день добавляя по одному сценарию, можно будет успевать как работать над своим кодом так и покрывать его тестами. Таким образом к моменту, когда ты будешь готов предоставить свою работу на обзор другим, код уже будет покрыт тестами, и чем тщательнее ты проработал сценарии покрытия, тем меньше багов уйдет в готовую работу.

Как оформить описание к Pull Request

Как правильно писать описания к запросам слияния, также известным как “Pull Request”?

Вторая концепция звучит примерно так: “Больше тестов богу тестов”, или же “Код тестами не испортишь”.

Как говорилось ранее, количество багов обратно пропорционально покрытию тестами. Можно написать пару общих тестов, чтобы проверить успешность исполнения программы, а вот функционал не отработать как следует. Но это плохая идея.

Как известно, дьявол кроется в мелочах. И порой такая мелочь может полностью испортить всю твою работу. У меня было несколько случаев, когда так и произошло. Тогда я и понял, усвоил для себя, что проверять надо все.

В одном случае вся опасность и суть бага заключалась в том, что из-за параллелизма наш код находился в состоянии гонки, когда один поток передавал данные быстрее другого. Это и приводило к поломке.

Второй же случай был более скрыт и только добавление сценария отказа сервиса позволило выявить багу. Поэтому всегда все покрывай тестами. Думаешь что здесь может скрываться баг? Напиши тест. Сомневаешься в том что RPC запрос отработает нормально? Пиши тест. А лучше — пиши несколько тестов, чтобы обложить его со всех сторон и выявить, где же прячется проблема. Сомневаешься в своей функции? Да, ты знаешь ответ. Пиши несколько сценариев, которые будут гонять твою функцию в хвост и гриву.

Не бойся писать тесты. Это может оказаться долгим и муторным процессом. Ведь ты должен писать одно и тоже, делая изменения практически в мелочах. Но именно в этих мелочах, в этих кирпичиках, что ты кладешь на свой фундамент, и кроется одна из главных концепций хорошего кода. Она кроется в том, что твой код протестирован до самых мелких деталей, так что ты просто не оставляешь шансов ошибке где-то спрятаться.

Готовим логи в Go проекте. Часть 1

Два аспекта логирования, а именно формат логов в проекте и применение уровней логирования. Как и где их правильно применять?

И третья концепция — самая болезненная и съевшая столько моего времени — это выбор формата тестов.

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

То же самое касается и тестов, но тут кроется один подводный камень. Даже если твой код будет чист, как слеза младенца, это не значит, что он будет легко читаем. Ведь до него уже написаны десятки сценариев, и количество строк может превышать тысячу, и из них ты, возможно, написал меньше половины. В таком случае нужно включить в тестирование все прошлые концепции. Продумай, возможно, даже пропиши на бумаге все или как минимум большую часть своих сценариев, чтобы определить, как именно ты будешь тестировать код. Для этого задай себе следующие вопросы:

  • ЧТО ты хочешь тестировать?
  • КАК ты хочешь это протестировать?
  • ЗАЧЕМ ты хочешь это протестировать?

Теперь посмотри на свою работу и продумай свои следующие действия. Готово? Теперь задай себе еще один вопрос: КАК мне сделать мои тесты легкими и понятными?

Ответ будет невероятно прост: Имена. Имя сценария, имя заглушки, имя проверки. Просто посмотрев на эти три вещи, человек уже должен понять что делает конкретно этот тест, что он проверяет, чего от него ожидать. И в случае какой-либо поломки репорт моментально тебе укажет едва ли не строчку кода, где произошла проблема. Это одна из вещей что облегчит как твой код, так и его чтение.

Есть еще один момент. Как я говорил ранее, написание тестов муторно еще и потому, что ты вынужден писать заглушки едва ли не под копирку. Так почему бы не облегчить процесс как себе, так и тому кто будет в будущем читать или, возможно, дополнять твой код?

Абсолютно все заглушки будет объединять одна вещь. У них у всех будет одинаковый запрос и ответ. Он может меняться в мелочах, но суть останется неизменной. Например, у нас есть RPC запросы, которые повторяются в многих обработчиках. Чтобы не писать везде одно и тоже, можно было просто пройтись по этим обработчикам, посмотреть в чем их различия, а потом в общем файле, к которому у всех будет доступ, написать функцию запроса и ответа.

Различия в этих самых запросах и ответах легко решить указанием параметров (возможно, в динамических языках такое не сработает, тогда прощу прощения. У меня есть опыт лишь работы со статическим языком программирования). Эти же параметры решат одновременно и проблему разных сценариев. В зависимости от сценария достаточно будет указать нужный параметр функции, и тогда заглушка отработает так, как мы того ожидаем.

То же самое касается и проверок пришедших данных. При проверке всего кода такие проверки будут в общих чертах схожими, они будут отличаться лишь в том месте, которое мы тестируем.

Так зачем писать одну и туже проверку по десять раз, когда мы можем запросто вынести их в один файл, а потом просто заново переиспользовать в нужных местах? Ведь не просто так программисты вывели две концепции: DRY(Don’t Repeat Yourself) и KISS(Keep It Simple Stupid). Тем самым уменьшается количество кода в тестах, он становится более читаем, и в случае чего очень просто дописать пару-тройку сценариев, внеся минимальные изменения, которые не затронут все остальные тесты.

Какое именование файлов тестов принято в golang

Я хочу создать пакет в Go с тестами и примерами для пакета в виде подкаталогов, чтобы рабочее пространство было чище. Возможно ли это, и если да, то как?

Вся документация всегда помещает тестовый код в то же место, что и другой код, это лучше в чем-то или просто соглашение?

Обратите внимание, что вы можете запускать go test «рекурсивно»: вам нужно перечислить все пакеты, которые вы хотите протестировать .

Если вы находитесь в корневой папке проекта Go, введите:

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

Такой шаблон распространяется на все каталоги пакетов, найденные в GOPATH деревьях с именами, соответствующими шаблонам.

Как особый случай, x/. совпадения, x а также x подкаталоги.
Например, net/. расширяется до net пакетов и в своих подкаталогах.

Если вы храните _test.go файлы во вложенной папке, команда « go test ./. » сможет их забрать.
Но:

  • вам нужно будет добавить к экспортируемым переменным и функциям (используемым в ваших тестах) префикс имени вашего пакета, чтобы тестовый файл мог получить доступ к экспортированному содержимому пакета.
  • у вас не будет доступа к неэкспортируемому контенту.

При этом я все же предпочел бы хранить _test.go файл рядом с основным исходным файлом: его легче найти.

Поместите свои тесты вместе с кодом в тот же каталог в файле с именем, file_test.go где «файл» — это имя файла исходного кода, который вы тестируете. Это условность, и я на собственном опыте убедился, что это лучше всего.

Если go test инструмент недостаточно автоматизирован для вас, вы можете изучить GoConvey , у которого есть веб-интерфейс, который автоматически обновляет и запускает традиционные тесты Go, а также тесты GoConvey (которые основаны на поведении и являются более самодокументированными. чем традиционные тесты Go).

Идеальная настройка вашего Golang проекта

При запуске нового проекта самые большие трудности у меня всегда вызывала его настройка. Всегда стараешься сделать её «идеальной»:

  • используешь лучшую структуру каталогов, чтобы всё было легко найти и импортирование происходило без проблем;
  • настраиваешь все команды так, чтобы нужные действия выполнялись в один клик или с вводом одной команды;
  • находишь лучший инструмент контроля качества кода, средство форматирования, среду тестирования для используемого в проекте языка и библиотеки…

Этот список можно продолжать и продолжать, и всё равно до идеальной настройки будет ещё далеко… Но, по моему скромному мнению, эта настройка для Golang просто лучшая!

Она так хорошо себя проявляет отчасти и потому, что основана на существующих проектах, которые вы можете найти здесь и тут.

Структура каталогов

Первым делом обратимся к структуре каталогов нашего проекта. Здесь у нас несколько файлов верхнего уровня и четыре каталога:

  • pkg — это пакет Go, который содержит только строку версии global. Меняется на версию из хэша текущей фиксации при проведении сборки;
  • config — конфигурационный каталог, который содержит файлы со всеми необходимыми переменными среды. Вы можете использовать любой тип файла, но я бы рекомендовал файлы YAML: их проще читать;
  • build — в этой директории у нас все скрипты оболочки, необходимые для сборки и тестирования приложения, а также создания отчётов для инструментов анализа кода;
  • cmd — фактический исходный код. По правилам именования исходный каталог называется cmd . Внутри есть ещё один каталог с именем проекта (в нашем случае blueprint ). В свою очередь, внутри этого каталога находится main.go , запускающий всё приложение. Также здесь можно найти все остальные исходные файлы, разделённые на модули (подробнее об этом далее).

Оказывается, многие предпочитают помещать исходный код в каталоги internal и pkg . Я думаю, что это лишнее: достаточно использовать для этого cmd , где для всего есть своё место.

Помимо каталогов, есть ещё большое количество файлов, о которых мы поговорим в статье.

Модули Go для идеального управления зависимостями

В проектах Go используются самые разные стратегии управления зависимостями. Однако с версии 1.11 Go обзавёлся официальным решением. Все наши зависимости приводятся в файле go.mod , в корневом каталоге. Вот как он может выглядеть:

Вы можете спросить: «А как в этот файл включить зависимости?». Очень просто, всего одной командой:

Эта команда переустанавливает vendor каталог основного модуля для включения всех пакетов, необходимых для сборки и тестирования каждого пакета модуля исходя из состояния файлов go.mod и исходного кода Go.

Фактический исходный код и конфигурация

И вот наконец мы добрались до исходного кода. Как уже говорилось, исходный код разделён на модули. Модуль представляет собой каталог внутри исходного корневого каталога. В каждом модуле находятся исходные файлы вместе с соответствующими файлами тестов. Например:

Такая структура способствует лучшей читаемости и лёгкости сопровождения кода: он идеально разделён на части, которые проще просматривать. Что касается конфигурации, в этой настройке используем библиотеку конфигураций Go Viper, которая может иметь дело с разными форматами, параметрами командной строки, переменными среды и т.д.

Посмотрим, как мы используем этот Viper здесь. Вот пакет config :

Он состоит из единственного файла. Объявляет один struct , который содержит все переменные конфигурации и имеет одну функцию LoadConfig , которая загружает конфигурацию. Требуется путь до конфигурационных файлов, в нашем случае используем путь до каталога config , который находится в корневом каталоге проекта и содержит наши YAML файлы. И как их будем использовать? Запустим первым делом в main.go :

Простое и быстрое тестирование

Что важнее всего после кода? Тесты. Чтобы писать много хороших тестов, нужна настройка, с которой это будет делать легко. Для этого мы используем цель Makefile под названием test , которая собирает и выполняет все тесты в подкаталогах cmd (все файлы с расширением _test.go ). Эти тесты кэшируются, так что их запуск происходит только при наличии изменений в соответствующей части кода. Это очень важно: если тесты будут слишком медленными, рано или поздно вы перестанете их запускать и сопровождать. Помимо модульного тестирования, make test помогает следить за общим качеством кода, запуская с каждым тестовым прогоном gofmt и go vet . gofmt способствует правильному форматированию кода, а go vet помогает с помощью эвристических алгоритмов выявлять в коде любые подозрительные конструкции. Вот пример того, что может получиться в результате выполнения:

Запуск всегда в Docker

Многие говорят, что у них запуск невозможен в облаке, а только на компьютере. Здесь есть простое решение: всегда запускаться в контейнере docker . Делаете ли вы сборку, запускаете ли или тестируете — делайте всё это в контейнере. Кстати, что касается тестирования, make test выполняется тоже только в docker .

Посмотрим, как это происходит. Начнём с файлов Dockerfile из корневого каталога проекта: один из них для тестирования ( test.Dockerfile ), а другой — для запуска приложения ( in.Dockerfile ):

  • test.Dockerfile — в идеале нам было бы достаточно одного файла Dockerfile для запуска приложения и тестирования. Но во время тестовых прогонов нам может потребоваться внести небольшие изменения в среде выполнения, поэтому у нас здесь есть образ для установки дополнительных инструментов и библиотек. Предположим, например, что мы подключаемся к базе данных. Нам не нужно поднимать весь PostgreSQL-сервер при каждом тестовом прогоне или зависеть от какой-нибудь базы данных на хост-машине. Мы просто используем для тестовых прогонов базу данных в памяти SQLite. И если дополнительные установки не понадобятся нашим тестам, то двоичным данным в SQLite они будут очень даже кстати: устанавливаем gcc и g++ , переключаем флажок на CGO_ENABLED , и готово.
  • in.Dockerfile — если посмотреть на этот Dockerfile в репозитории, что мы увидим: просто несколько аргументов и копирование конфигурации в образ. Но что здесь происходит? in.Dockerfile используется только из Makefile (заполненного аргументами), когда мы запускаем make container . Давайте теперь обратимся в сам Makefile , Всё, что связано с docker , делает для нас именно он. ��

Связываем всё вместе с помощью Makefile

Долгое время Make-файлы казались мне страшными (до этого я сталкивался с ними лишь при работе с кодом C ), но на самом деле ничего страшного здесь нет, и их много где можно использовать, в том числе для этого проекта! Посмотрим, какие цели у нас здесь есть в Makefile :

  • make test — первая в рабочем потоке — собранное приложение — создаёт исполняемый двоичный код в каталоге bin :
  • make test — тестовая — она снова использует почти тот же docker run , только здесь ещё есть скрипт test.sh (покажем только то, что нас интересует):

Эти строчки — важная часть файла. Первая из них собирает тестовые цели, где в качестве параметра указан путь. Вторая строчка запускает тесты и выводит документацию по тестированию ПО. Оставшиеся две строчки запускают gofmt и go vet . Они собирают и выводят ошибки (если таковые имеются).

  • make container — и, наконец, важнейшая часть — создание развёртываемого контейнера:

Код для этой цели довольно прост: сначала он подставляет переменные в in.Dockerfile , а затем запускает docker build для получения образа с «изменёнными» и «последними» тегами. И дальше передаёт имя контейнера на стандартный вывод.

  • Теперь, когда у нас есть образ, нужно где-то его хранить. С этим нам помогает make push , который помещает образ в хранилище образов Docker registry.
  • make ci — ещё один способ использовать Makefile — задействовать его в процессах непрерывной интеграции и развёртывания приложений (об этом речь пойдёт далее). Эта цель очень похожа на make test : тоже запускает все тесты и плюс к этому генерирует отчёты о покрытии, которые потом используются как вводная информация при проведении анализа кода.
  • make clean — и, наконец, если нам нужно провести очистку проекта, запускаем make clean , который удалит все файлы, сгенерированные предыдущими целями.

Остальные цели можно объединить в две группы: первые не так важны для нормального рабочего процесса, а вторые являются лишь частью других целей, поэтому о них можно не упоминать.

Интеграция и развёртывание ПО для идеальной разработки

Завершаем статью важной частью — процессом непрерывной интеграции и развёртывания приложений. Не буду подробно расписывать, что в нем такого — вы и сами прекрасно сможете разобраться (практически в каждой строке есть комментарий, так что всё должно быть понятно):

Но кое-что можно прояснить.

В этой сборке Travis использована сборка Matrix Build с 4 параллельными заданиями для ускорения всего процесса:

  • Сборка и тестирование: здесь мы проверяем, что приложение работает как надо;
  • SonarCloud: здесь мы генерируем отчёты о покрытии и отправляем их на сервер SonarCloud;
  • CodeClimate: здесь — как и в предыдущем задании — мы генерируем отчёты о покрытии и отправляем их, только на этот раз в CodeClimate с помощью их генератора тестовых отчётов;
  • Push to Registry: и, наконец, помещаем наш контейнер в хранилище GitHub Registry.

Заключение

Надеюсь, эта статья поможет вам в ваших будущих разработках кода на Go. Все подробности изложены в репозитории.

В следующей части узнаем, как на базе этого макета проекта, который мы сегодня выстроили, с лёгкостью создавать интерфейсы RESTful API, тестировать с базой данных в памяти, а также настраивать крутую документацию (а пока можно подсмотреть в ветке rest-api репозитория). ��

Пойдем: тестирование программ Golang

В этом уроке я научу вас всем основам идиоматического тестирования в Go, используя лучшие практики, разработанные дизайнерами языков и сообществом. Основным оружием станет стандартный тестовый пакет. Целью будет пример программы, которая решает простую задачу из Project Euler .

Go – невероятно мощный язык программирования, изучите все от написания простых утилит до создания масштабируемых, гибких веб-серверов в нашем полном курсе.

Квадрат Сумма Разница

Проблема разности суммы квадратов довольно проста: «Найдите разницу между суммой квадратов первых ста натуральных чисел и квадратом суммы».

Эта конкретная проблема может быть решена довольно кратко, особенно если вы знаете своего Гаусса. Например, сумма первых N натуральных чисел равна (1 + N) * N / 2 , а сумма квадратов первых N целых чисел равна: (1 + N) * (N * 2 + 1) * N / 6 Таким образом, вся проблема может быть решена с помощью следующей формулы и присвоением 100 N:

(1 + N) * (N * 2 + 1) * N / 6 — ((1 + N) * N / 2) * ((1 + N) * N / 2)

Ну, это очень конкретно, и тестировать особо нечего. Вместо этого я создал несколько функций, которые являются более общими, чем то, что необходимо для этой проблемы, но они могут служить для других программ в будущем (у проекта Euler сейчас 559 проблем).

Код доступен на GitHub .

Вот подписи четырех функций:

Теперь, с нашей целевой программой (пожалуйста, прости меня, фанаты TDD), давайте посмотрим, как писать тесты для этой программы.

Пакет тестирования

Пакет тестирования идет рука об руку с командой go test . Ваши тесты пакетов должны идти в файлах с суффиксом “_test.go”. Вы можете разделить свои тесты на несколько файлов, которые следуют этому соглашению. Например: “what1_test.go” и “what2_test.go”. Вы должны поместить свои тестовые функции в эти тестовые файлы.

Каждая тестовая функция является публично экспортируемой функцией, имя которой начинается с «Test», принимает указатель на объект testing.T и ничего не возвращает. Это выглядит как:

Объект T предоставляет различные методы, которые вы можете использовать для указания сбоя или записи ошибок.

Помните: команда go test выполняет только тестовые функции, определенные в тестовых файлах.

Письменные тесты

Каждый тест выполняется в одном и том же порядке: настройте тестовую среду (необязательно), введите код тестового ввода, запишите результат и сравните его с ожидаемым результатом. Обратите внимание, что входные данные и результаты не должны быть аргументами функции.

Если тестируемый код извлекает данные из базы данных, то входные данные будут обеспечивать, чтобы база данных содержала соответствующие тестовые данные (что может включать в себя насмешку на различных уровнях). Но для нашего приложения достаточно обычного сценария передачи входных аргументов в функцию и сравнения результата с выходными данными функции.

Давайте начнем с функции SumList() . Эта функция берет часть целых чисел и возвращает их сумму. Вот тестовая функция, которая проверяет, что SumList() ведет себя как следует.

Он тестирует два тестовых случая, и, если ожидаемый результат не соответствует результату, он вызывает метод Error() объекта testing.T.

Это все просто, но выглядит немного многословно. Тестирование Idiomatic Go использует табличные тесты, в которых вы определяете структуру для пар входов и ожидаемых выходов, а затем получаете список этих пар, которые вы передаете в цикле к одной и той же логике. Вот как это делается для тестирования функции SumList() .

Это намного лучше. Легко добавить больше тестов. Легко иметь полный спектр тестовых случаев в одном месте, и если вы решите изменить логику теста, вам не нужно менять несколько экземпляров.

Вот еще один пример для тестирования функции SquareList() . В этом случае и вход, и выход являются кусочками целых чисел, поэтому структура тестовой пары отличается, но поток идентичен. Здесь интересно то, что Go не предоставляет встроенного способа сравнения reflect.DeepEqual() , поэтому я использую reflect.DeepEqual() для сравнения выходного слайса с ожидаемым слайсом.

Запуск тестов

Запускать тесты так же просто, как набирать go test в каталоге вашего пакета. Go найдет все файлы с суффиксом “_test.go” и все функции с префиксом “Test” и запустит их как тесты. Вот как это выглядит, когда все в порядке:

Не очень драматично. Позвольте мне специально пройти тест. Я изменю контрольный пример для SumList() таким образом, чтобы ожидаемые выходные данные для суммирования 1 и 2 составляли 7.

Теперь, когда вы набираете go test , вы получаете:

Это довольно хорошо описывает, что произошло, и должно дать вам всю информацию, необходимую для решения проблемы. В этом случае проблема заключается в том, что сам тест неверен и ожидаемое значение должно быть 3. Это важный урок. Не предполагайте автоматически, что если тест не пройден, тестируемый код нарушается. Рассмотрим всю систему, которая включает тестируемый код, сам тест и среду тестирования.

Тестовое покрытие

Чтобы убедиться, что ваш код работает, недостаточно пройти тестирование. Другим важным аспектом является тестовое покрытие. Ваши тесты охватывают каждое утверждение в коде? Иногда даже этого недостаточно. Например, если в вашем коде есть цикл, который выполняется до тех пор, пока не будет выполнено условие, вы можете успешно проверить его с помощью условия, которое работает, но не замечаете, что в некоторых случаях условие всегда может быть ложным, что приводит к бесконечному циклу ,

Модульные тесты

Модульные тесты похожи на чистку зубов и зубную нить. Вы не должны пренебрегать ими. Они являются первым барьером против проблем и позволят вам быть уверенным в рефакторинге. Они также полезны, когда пытаются воспроизвести проблемы и могут написать неудачный тест, который демонстрирует проблему, которая проходит после устранения проблемы.

Интеграционные тесты

Интеграционные тесты также необходимы. Думайте о них как о посещении дантиста. Вы можете быть в порядке без них некоторое время, но если вы пренебрегаете ими слишком долго, это не будет красиво.

Большинство нетривиальных программ состоят из нескольких взаимосвязанных модулей или компонентов. Проблемы могут часто возникать при соединении этих компонентов вместе. Интеграционные тесты дают вам уверенность в том, что вся ваша система работает так, как задумано. Существует много других типов тестов, таких как приемочные тесты, тесты производительности, нагрузочные тесты и полноценные системные тесты, но модульные тесты и интеграционные тесты являются двумя основными способами тестирования программного обеспечения.

Вывод

Go имеет встроенную поддержку для тестирования, четко определенный способ написания тестов и рекомендуемые рекомендации в виде табличных тестов.

Потребность в написании специальных структур для каждой комбинации входов и выходов немного раздражает, но это цена, которую вы платите за простой конструктивный подход Go.

Похожие публикации:

  1. Как сохранить график в матлабе в виде картинки
  2. Как сохранить обои на телефоне в галерею
  3. Как сохранить стиль в word
  4. Как стилизовать select option css

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *