В статье «Как эффективно тестировать код Go» мы говорили о модульном тестировании и предлагали решения для интерфейсных и фиктивных зависимостей, позволяющие решить две основные проблемы: развязку и зависимость. В то же время в этой статье также обсуждаются некоторые практические инструменты тестирования в области Go, читатели могут ее прочитать. В центре внимания модульного тестирования находится логическая единица кода, обычно объект или конкретная функция. Мы можем написать достаточное количество модульных тестов, чтобы гарантировать качество кода. Когда функции изменяются или код подвергается рефакторингу, достаточное количество случаев модульного тестирования может дать нам достаточную уверенность. Над модульными тестами находятся спецификации разработки. В гибкой разработке программного обеспечения часто встречаются два человека: разработка через тестирование (TDD) и разработка через поведение (BDD). Это не только практики и методы, но и методологии проектирования.
Основная идея TDD — продвигать всю разработку посредством тестирования. Принцип состоит в том, чтобы писать сценарии модульного тестирования до разработки функционального кода. Содержит следующие пять шагов:
Когда необходимо разработать новые функции, описанные выше шаги повторяются. Процесс выглядит так, как показано ниже.
Есть интересный репозиторий на Github: Learn-go-with-tests, предназначенный для изучения TDD с помощью Go.
TDD фокусируется на разработке и использует тестовые примеры, чтобы регулировать и ограничивать разработчиков, чтобы они писали код более высокого качества и с меньшим количеством ошибок. BDD больше внимания уделяет проектированию. Он требует, чтобы система была определена при разработке тестовых примеров, выступает за использование общего языка для описания поведения системы и объединение проектирования системы и тестовых примеров для управления работой по разработке. BDD является производным от TDD, и основное отличие заключается в описании тестов. BDD использует более понятный язык для описания тестовых случаев, уделяя больше внимания требуемой функциональности, а не фактическим результатам. Возможность, предоставляемая BDD, читать тесты как предложения, приводит к когнитивным изменениям в тестировании и помогает нам думать о том, как писать более эффективные тесты.
Ginkgo — это среда тестирования BDD на языке Go, призванная помочь разработчикам писать выразительные и комплексные тесты. Ginkgo интегрируется с собственными библиотеками Go, что означает, что вы можете запускать набор тестов Ginkgo через . В то же время он также совместим с набором утверждений и макетов, а также с богатым набором тестов для проверки. Но Ginkgo рекомендует использовать его с библиотекой gomega.
10 часто используемых модулей Ginkgo: It, Context, Describe, BeforeEach, AfterEach, JustBeforeEach, BeforeSuite, AfterSuite, By, Fail
go get github.com/onsi/ginkgo/ginkgo
go get github.com/onsi/gomega/...
Создайте тестовую папку, например, example, войдите в папку, выполните команду ginkgo bootstrap, чтобы сгенерировать файл шаблона, имя файла — example_suite_test.go, в нем есть функция ввода для выполнения примера создания гинкго, пример не требуется. написано, по умолчанию используется имя текущей папки, сгенерируйте тест. Пример файла шаблона example_test.go добавляет суффикс _test, чтобы отличить его от существующего кода в текущей папке. Код example_test.go по умолчанию импортирует текущую папку.
var _ = Describe(“Book”, func() {
var (
book Book
err error
json string
)
BeforeEach(func() {
json = `{
"title":"Les Miserables",
"author":"Victor Hugo",
"pages":1488
}`
})
JustBeforeEach(func() {
book, err = NewBookFromJSON(json)
})
AfterEach(func() {
By("End One Test")
})
Describe("loading from JSON", func() {
Context("when the JSON parses succesfully", func() {
It("should populate the fields correctly", func() {
Expect(book.Title).To(Equal("Les Miserables"))
Expect(book.Author).To(Equal("Victor Hugo"))
Expect(book.Pages).To(Equal(1488))
})
It("should not error", func() {
Expect(err).NotTo(HaveOccurred())
})
})
Context("when the JSON fails to parse", func() {
BeforeEach(func() {
json = `{
"title":"Les Miserables",
"author":"Victor Hugo",
"pages":1488oops
}`
})
It("should return the zero-value for the book", func() {
Expect(book).To(BeZero())
})
It("should error", func() {
if err != nil {
Fail("This Case Failed")
}
})
})
})
Describe("Extracting the author's last name", func() {
It("should correctly identify and return the last name", func(){
Expect(book.AuthorLastName()).To(Equal("Hugo"))
})
})
})
Как видите, сначала определяются глобальные переменные book, err и json. Пять тестовых примеров разделены на две основные категории, различающиеся двумя описаниями. Первая категория разделена на две небольшие категории, различающиеся контекстом. Каждый It содержит тестовый пример. Состоит из двух BeforeEach, каждый из которых работает только в пределах текущего домена. Последовательность выполнения — это последовательное выполнение одного и того же уровня и выполнение разных уровней от внешнего слоя к внутреннему. AfterEach правило меняется на противоположное. AfterEach обычно используется для очистки данных после завершения выполнения тестового примера, а также может использоваться для оценки результатов. Старайтесь не присваивать значения переменным в var, поскольку каждый раз при выполнении тестового примера значение глобальная переменная может быть изменена, что вызовет проблемы для последующих тестовых примеров Influence, ее правильнее записать в BeforeEach.
func TestBooks(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Books Suite")
}
var _ = BeforeSuite(func() {
dbRunner = db.NewRunner()
err := dbRunner.Start()
Expect(err).NotTo(HaveOccurred())
dbClient = db.NewClient()
err = dbClient.Connect(dbRunner.Address())
Expect(err).NotTo(HaveOccurred())
})
var _ = AfterSuite(func() {
dbClient.Cleanup()
dbRunner.Stop()
})
BeforeSuite и AfterSuite записаны в файле _suite_test.go и будут выполняться до и после выполнения всех тестовых случаев. Если BeforeSuite не запускается, этот набор тестов не будет выполнен.
Совет: При использовании C для прерывания выполнения AfterSuite все равно будет выполняться, и вам придется использовать C для прерывания еще раз.
Их три: Ф,
FDescribe(“outer describe”, func() {
It(“A”, func() { … })
It(“B”, func() { … })
})
Совет: Если фокус существует как во внутреннем, так и во внешнем слоях, внешний слой недействителен, то есть следующий код выполнит только тестовый пример B.
FDescribe(“outer describe”, func() {
It(“A”, func() { … })
FIt(“B”, func() { … })
})
Значение P — «Ожидание», то есть не выполняется. Использование такое же, как F. Внешний уровень правила вступает в силу. X имеет то же значение, что и P. Другой способ пропустить тестовые примеры — добавить его.
Skip It(“should do something, if it can”, func() {
if !someCondition {
Skip(“special condition wasn’t met”)
}
})
ginkgo -p использует количество параллелизма по умолчанию, ginkgo -nodes=N Установите количество параллелизма самостоятельно. Число параллелизма по умолчанию — это значение параметра runtime.NumCPU(), которое представляет собой количество логических процессоров. Если оно больше 4, используйте runtime.NumCPU()-1. во время одновременного выполнения суммируются и объединяются. Он обрабатывается, а затем печатается, поэтому он выглядит более стандартизированным. Содержимое каждого тестового примера также печатается вместе, но не в реальном времени. Если вам нужно печатать в реальном времени, добавьте. параметр -stream. Недостаток заключается в том, что каждый журнал тестовых случаев печатается перекрестно.
It(“should post to the channel, eventually”, func(done Done) {
c := make(chan string, 0)
go DoSomething(c)
Expect(<-c).To(ContainSubstring("Done!"))
close(done)
}, 0.2)
Гинкго обнаруживает параметр типа «Готово»,Тайм-аут будет установлен автоматически,Всего на 0,2 позади да,Единица измерения в секунду
Иногда многие тестовые примеры одинаковы, за исключением части данных. Было бы затруднительно писать много подобных, поэтому появляется формат таблицы.
package table_test
import (
. “github.com/onsi/ginkgo/extensions/table”
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe(“Math”, func() {
DescribeTable(“the > inequality”,
func(x int, y int, expected bool) {
Expect(x > y).To(Equal(expected))
},
Entry(“x > y”, 1, 0, true),
Entry(“x == y”, 0, 0, false),
Entry(“x < y”, 0, 1, false),
)
})
Эквивалентно
package table_test import (
. “github.com/onsi/ginkgo”
. “github.com/onsi/gomega”
)
var _ = Describe(“Math”, func() {
Describe(“the > inequality”,
It(“x > y”, func() {
Expect(1 > 0).To(Equal(true))
})
It("x == y", func() {
Expect(0 > 0).To(Equal(false))
})
It("x < y", func() {
Expect(0 > 1).To(Equal(false))
})
)
})
Обычно генерирует отчет о тестировании Junit XML
func TestFoo(t *testing.T) {
RegisterFailHandler(Fail)
junitReporter := reporters.NewJUnitReporter("junit.xml")
RunSpecsWithDefaultAndCustomReporters(t, "Foo Suite", []Reporter{junitReporter})
}
Использование модуля «Измерение»
Measure(“it should do something hard efficiently”, func(b Benchmarker) {
runtime := b.Time(“runtime”, func() {
output := SomethingHard()
Expect(output).To(Equal(17))
})
Ω(runtime.Seconds()).Should(BeNumerically("<", 0.2), "SomethingHard() shouldn't take too long.")
b.RecordValue("disk usage (in MB)", HowMuchDiskSpaceDidYouUse())
}, 10)
Этот тестовый пример будет запущен 10 раз, и данные о производительности выполнения будут распечатаны.
• [MEASUREMENT]
Suite
it should do something hard efficiently
Ran 10 samples:
runtime:
Fastest Time: 0.01s
Slowest Time: 0.08s
Average Time: 0.05s ± 0.02s
disk usage (in MB):
Smallest: 3.0
Largest: 5.2
Average: 3.9 ± 0.4
DD и BDD — это методологии, часто упоминаемые в гибкой разработке. По сравнению с TDD, BDD стимулирует разработку программного обеспечения путем написания поведения и спецификаций. Такое поведение и спецификации отражаются в более «подробном» описании кода. Есть еще один способ выразить суть BDD: BDD помогает разработчикам разрабатывать программное обеспечение, а TDD помогает разработчикам тестировать программное обеспечение. Ginkgo — отличная платформа BDD на языке Go. Она эффективно помогает разработчикам организовывать и организовывать тестовые сценарии с помощью синтаксиса DSL (Describe/Context/It). В этой статье показан только очень простой вариант использования гинкго, и ее следует использовать в качестве отправной точки. При использовании Ginkgo читателям необходимо понимать жизненный цикл его выполнения, уделяя особое внимание последовательности выполнения и семантической логике этих модулей. Ginkgo имеет множество функций, не описанных в этой статье, таких как асинхронное тестирование, эталонное тестирование, непрерывная интеграция и другая мощная поддержка. Его склад расположен по адресу https://github.com/onsi/ginkgo. Он также предоставляет английскую и китайскую версии документации по использованию. Читатели могут использовать его, чтобы узнать больше о Ginkgo. Наконец, платформа Ginkgo также используется в проекте K8s для написания сквозных (E2E) тестовых примеров, чему стоит поучиться.
Ссылка: https://view.inews.qq.com/a/20220711A0270R00.