SlideShare a Scribd company logo
How to not write a boring testHow to not write a boring testHow to not write a boring test
Dan TranDan TranDan Tran
Development and TestingDevelopment and TestingDevelopment and Testing
222
Test Driven DevelopmentTest Driven DevelopmentTest Driven Development
Write testWrite testWrite test
Write codeWrite codeWrite code 333
Unit testingUnit testingUnit testing
Table-driven testTable-driven testTable-driven test
StubStubStub
MockingMockingMocking 444
Table-driven testTable-driven testTable-driven test
// Gcd returns the greatest common divisor of 2 numbers.// Gcd returns the greatest common divisor of 2 numbers.// Gcd returns the greatest common divisor of 2 numbers.
func Gcd(a, b int) int {func Gcd(a, b int) int {func Gcd(a, b int) int {
if a%2 == 0 {if a%2 == 0 {if a%2 == 0 {
if b%2 == 0 {if b%2 == 0 {if b%2 == 0 {
return 2 * Gcd(a/2, b/2)return 2 * Gcd(a/2, b/2)return 2 * Gcd(a/2, b/2)
}}}
return Gcd(a/2, b)return Gcd(a/2, b)return Gcd(a/2, b)
}}}
if b%2 == 0 {if b%2 == 0 {if b%2 == 0 {
return Gcd(a, b/2)return Gcd(a, b/2)return Gcd(a, b/2)
}}}
if a == b {if a == b {if a == b {
return areturn areturn a
}}}
if a > b {if a > b {if a > b {
return Gcd(a-b, b)return Gcd(a-b, b)return Gcd(a-b, b)
}}}
return Gcd(b-a, a)return Gcd(b-a, a)return Gcd(b-a, a)
}}} Run
555
Table-driven test (2)Table-driven test (2)Table-driven test (2)
func TestGcd(t *testing.T) {func TestGcd(t *testing.T) {func TestGcd(t *testing.T) {
tests := []struct {tests := []struct {tests := []struct {
name stringname stringname string
args argsargs argsargs args
want intwant intwant int
}{}{}{
{{{
"Test two different numbers","Test two different numbers","Test two different numbers",
args{a: 4, b: 6},args{a: 4, b: 6},args{a: 4, b: 6},
2,2,2,
},},},
{{{
"Test two identical numbers","Test two identical numbers","Test two identical numbers",
args{a: 5, b: 5},args{a: 5, b: 5},args{a: 5, b: 5},
5,5,5,
},},},
}}}
for _, tt := range tests {for _, tt := range tests {for _, tt := range tests {
tt := tttt := tttt := tt
t.Run(tt.name, func(t *testing.T) {t.Run(tt.name, func(t *testing.T) {t.Run(tt.name, func(t *testing.T) {
if got := Gcd(tt.args.a, tt.args.b); got != tt.want {if got := Gcd(tt.args.a, tt.args.b); got != tt.want {if got := Gcd(tt.args.a, tt.args.b); got != tt.want {
t.Errorf("Gcd() = %v, want %v", got, tt.want)t.Errorf("Gcd() = %v, want %v", got, tt.want)t.Errorf("Gcd() = %v, want %v", got, tt.want)
}}}
})})})
}}}
}}} Run
666
ExampleExampleExample
package stripepackage stripepackage stripe
// Client ...// Client ...// Client ...
// go:generate mockery -name=Client -case=underscore// go:generate mockery -name=Client -case=underscore// go:generate mockery -name=Client -case=underscore
type Client interface {type Client interface {type Client interface {
GetBalance(customerID string, currency string) (int64, error)GetBalance(customerID string, currency string) (int64, error)GetBalance(customerID string, currency string) (int64, error)
Charge(customerID string, amount int64, currency string, desc string) errorCharge(customerID string, amount int64, currency string, desc string) errorCharge(customerID string, amount int64, currency string, desc string) error
}}} Run
777
Stub (1)Stub (1)Stub (1)
Create an implementation which has the same set of functions as the mainCreate an implementation which has the same set of functions as the mainCreate an implementation which has the same set of functions as the main
object/class.object/class.object/class.
type stubClient struct{}type stubClient struct{}type stubClient struct{}
// GetBalance ...// GetBalance ...// GetBalance ...
func (c *stubClient) GetBalance(customerID string, currency string) (int64, error) {func (c *stubClient) GetBalance(customerID string, currency string) (int64, error) {func (c *stubClient) GetBalance(customerID string, currency string) (int64, error) {
return 100, nilreturn 100, nilreturn 100, nil
}}}
// Charge ...// Charge ...// Charge ...
func (c *stubClient) Charge(customerID string, amount int64, currency string, desc string) errorfunc (c *stubClient) Charge(customerID string, amount int64, currency string, desc string) errorfunc (c *stubClient) Charge(customerID string, amount int64, currency string, desc string) error
return nilreturn nilreturn nil
}}}
// TestGetBalanceHappyPath// TestGetBalanceHappyPath// TestGetBalanceHappyPath Run
888
Stub (2)Stub (2)Stub (2)
func TestGetBalanceHappyPath(t *testing.T) {func TestGetBalanceHappyPath(t *testing.T) {func TestGetBalanceHappyPath(t *testing.T) {
stripeClient = &stubClient{}stripeClient = &stubClient{}stripeClient = &stubClient{}
t.Run("Happy path", func(t *testing.T) {t.Run("Happy path", func(t *testing.T) {t.Run("Happy path", func(t *testing.T) {
got, err := GetBalance(existingCustomerID)got, err := GetBalance(existingCustomerID)got, err := GetBalance(existingCustomerID)
if err != nil {if err != nil {if err != nil {
t.Errorf("GetBalance() error = %v, wantErr %v", err, nil)t.Errorf("GetBalance() error = %v, wantErr %v", err, nil)t.Errorf("GetBalance() error = %v, wantErr %v", err, nil)
returnreturnreturn
}}}
if got != 100 {if got != 100 {if got != 100 {
t.Errorf("GetBalance() = %v, want %v", got, 100)t.Errorf("GetBalance() = %v, want %v", got, 100)t.Errorf("GetBalance() = %v, want %v", got, 100)
}}}
})})})
}}} Run
999
Generate mockGenerate mockGenerate mock
Using mockery: go get github.com/vektra/mockery/...Using mockery: go get github.com/vektra/mockery/...Using mockery: go get github.com/vektra/mockery/...
mockery -name=Client -case=underscoremockery -name=Client -case=underscoremockery -name=Client -case=underscore
In test leIn test leIn test le
m := mocks.Client{}m := mocks.Client{}m := mocks.Client{}
m.On("GetBalance", mock.AnythingOfType("string"), mock.AnythingOfType("string")).m.On("GetBalance", mock.AnythingOfType("string"), mock.AnythingOfType("string")).m.On("GetBalance", mock.AnythingOfType("string"), mock.AnythingOfType("string")).
Return(int64(100), nil).Once()Return(int64(100), nil).Once()Return(int64(100), nil).Once()
101010
MockingMockingMocking
for _, scenario := range tests {for _, scenario := range tests {for _, scenario := range tests {
scenario := scenarioscenario := scenarioscenario := scenario
t.Run(scenario.name, func(t *testing.T) {t.Run(scenario.name, func(t *testing.T) {t.Run(scenario.name, func(t *testing.T) {
m := &mocks.Client{}m := &mocks.Client{}m := &mocks.Client{}
scenario.configureMock(m)scenario.configureMock(m)scenario.configureMock(m)
stripeClient = mstripeClient = mstripeClient = m
got, err := GetBalance(scenario.userID)got, err := GetBalance(scenario.userID)got, err := GetBalance(scenario.userID)
if (err != nil) != scenario.wantErr {if (err != nil) != scenario.wantErr {if (err != nil) != scenario.wantErr {
t.Errorf("GetBalance() error = %v, wantErr %v", err, scenario.wantErr)t.Errorf("GetBalance() error = %v, wantErr %v", err, scenario.wantErr)t.Errorf("GetBalance() error = %v, wantErr %v", err, scenario.wantErr)
returnreturnreturn
}}}
if got != scenario.want {if got != scenario.want {if got != scenario.want {
t.Errorf("GetBalance() = %v, want %v", got, scenario.want)t.Errorf("GetBalance() = %v, want %v", got, scenario.want)t.Errorf("GetBalance() = %v, want %v", got, scenario.want)
returnreturnreturn
}}}
})})})
}}}
}}} Run
111111
Dependency InjectionDependency InjectionDependency Injection
121212
Monkey patchingMonkey patchingMonkey patching
In api.goIn api.goIn api.go
In api_test.goIn api_test.goIn api_test.go
originalFn := getBalanceoriginalFn := getBalanceoriginalFn := getBalance
getBalance = func(string, string)(int, error){...}getBalance = func(string, string)(int, error){...}getBalance = func(string, string)(int, error){...}
defer func() {defer func() {defer func() {
getBalance = originalFngetBalance = originalFngetBalance = originalFn
}()}()}()
var getBalance = stripeClient.GetBalancevar getBalance = stripeClient.GetBalancevar getBalance = stripeClient.GetBalance
// GetBalance2 ...// GetBalance2 ...// GetBalance2 ...
func GetBalance2(userID string) (int64, error) {func GetBalance2(userID string) (int64, error) {func GetBalance2(userID string) (int64, error) {
currency := loadCurrencyByUser(userID)currency := loadCurrencyByUser(userID)currency := loadCurrencyByUser(userID)
bal, err := getBalance(userID, currency)bal, err := getBalance(userID, currency)bal, err := getBalance(userID, currency)
if err != nil {if err != nil {if err != nil {
return 0, errreturn 0, errreturn 0, err
}}}
return bal, nilreturn bal, nilreturn bal, nil
}}} Run
131313
Behaviour Driven DevelopmentBehaviour Driven DevelopmentBehaviour Driven Development
141414
Cucumber for golangCucumber for golangCucumber for golang
Feature: get versionFeature: get versionFeature: get version
In order to know godog versionIn order to know godog versionIn order to know godog version
As an API userAs an API userAs an API user
I need to be able to request versionI need to be able to request versionI need to be able to request version
Scenario: does not allow POST methodScenario: does not allow POST methodScenario: does not allow POST method
When I send "POST" request to "/version"When I send "POST" request to "/version"When I send "POST" request to "/version"
Then the response code should be 405Then the response code should be 405Then the response code should be 405
And the response should match json:And the response should match json:And the response should match json:
"""""""""
{{{
"error": "Method not allowed""error": "Method not allowed""error": "Method not allowed"
}}}
"""""""""
Scenario: should get version numberScenario: should get version numberScenario: should get version number
When I send "GET" request to "/version"When I send "GET" request to "/version"When I send "GET" request to "/version"
Then the response code should be 200Then the response code should be 200Then the response code should be 200
And the response should match json:And the response should match json:And the response should match json:
"""""""""
{{{
"version": "v0.7.13""version": "v0.7.13""version": "v0.7.13"
}}}
""""""""" Run
151515
Cucumber style - GodogCucumber style - GodogCucumber style - Godog
func FeatureContext(s *godog.Suite) {func FeatureContext(s *godog.Suite) {func FeatureContext(s *godog.Suite) {
api := &apiFeature{}api := &apiFeature{}api := &apiFeature{}
s.BeforeScenario(api.resetResponse)s.BeforeScenario(api.resetResponse)s.BeforeScenario(api.resetResponse)
s.Step(`^I send "(GET|POST|PUT|DELETE)" request to "([^"]*)"$`, api.iSendrequestTo)s.Step(`^I send "(GET|POST|PUT|DELETE)" request to "([^"]*)"$`, api.iSendrequestTo)s.Step(`^I send "(GET|POST|PUT|DELETE)" request to "([^"]*)"$`, api.iSendrequestTo)
s.Step(`^the response code should be (d+)$`, api.theResponseCodeShouldBe)s.Step(`^the response code should be (d+)$`, api.theResponseCodeShouldBe)s.Step(`^the response code should be (d+)$`, api.theResponseCodeShouldBe)
s.Step(`^the response should match json:$`, api.theResponseShouldMatchJSON)s.Step(`^the response should match json:$`, api.theResponseShouldMatchJSON)s.Step(`^the response should match json:$`, api.theResponseShouldMatchJSON)
}}} Run
161616
Other TipsOther TipsOther Tips
Make a habit: de ne a package with interface and mockMake a habit: de ne a package with interface and mockMake a habit: de ne a package with interface and mock
What I should test: functions or behaviours. Write happy path case rst.What I should test: functions or behaviours. Write happy path case rst.What I should test: functions or behaviours. Write happy path case rst.
Consider writting tests for public funcs than private func.Consider writting tests for public funcs than private func.Consider writting tests for public funcs than private func.
Create di erent build tag for unit and integration testsCreate di erent build tag for unit and integration testsCreate di erent build tag for unit and integration tests
// +build unit// +build unit// +build unit
// +build integration// +build integration// +build integration
Disabling cache with count=1Disabling cache with count=1Disabling cache with count=1 171717
Test coverageTest coverageTest coverage
go test -covermode=count -coverprofile=count.outgo test -covermode=count -coverprofile=count.outgo test -covermode=count -coverprofile=count.out
go tool cover -func=count.outgo tool cover -func=count.outgo tool cover -func=count.out
181818
Using git pre-commit hookUsing git pre-commit hookUsing git pre-commit hook
Edit local changes in .git/hooks/pre-commitEdit local changes in .git/hooks/pre-commitEdit local changes in .git/hooks/pre-commit
Trigger go lint/test before every commitTrigger go lint/test before every commitTrigger go lint/test before every commit
#!/bin/sh#!/bin/sh#!/bin/sh
# run go lint# run go lint# run go lint
# run unit tests# run unit tests# run unit tests
go test -count=1 -short ./...go test -count=1 -short ./...go test -count=1 -short ./...
if [[ $? == 1 ]]; thenif [[ $? == 1 ]]; thenif [[ $? == 1 ]]; then
printf "unit test failed"printf "unit test failed"printf "unit test failed"
exit 1exit 1exit 1
fififi
# run integration tests# run integration tests# run integration tests
# go test --tags=integration ./...# go test --tags=integration ./...# go test --tags=integration ./...
exit 0exit 0exit 0 Run
191919
ResourcesResourcesResources
Demo code:Demo code:Demo code: gitlab.com/aladine/testing-demogitlab.com/aladine/testing-demogitlab.com/aladine/testing-demo(https://gitlab.com/aladine/testing-demo)(https://gitlab.com/aladine/testing-demo)(https://gitlab.com/aladine/testing-demo)
Hands-On Dependency Injection in Go, Corey Scott:Hands-On Dependency Injection in Go, Corey Scott:Hands-On Dependency Injection in Go, Corey Scott: EbookEbookEbook
(https://subscription.packtpub.com/book/application_development/9781789132762)(https://subscription.packtpub.com/book/application_development/9781789132762)(https://subscription.packtpub.com/book/application_development/9781789132762)
https://github.com/DATA-DOG/godoghttps://github.com/DATA-DOG/godoghttps://github.com/DATA-DOG/godog
Arts icon byArts icon byArts icon by twitter.com/deniseyu21twitter.com/deniseyu21twitter.com/deniseyu21(https://twitter.com/deniseyu21)(https://twitter.com/deniseyu21)(https://twitter.com/deniseyu21)
202020
Hiring - OpenSimSimHiring - OpenSimSimHiring - OpenSimSim
We are hiring golang developers and devops engineers.We are hiring golang developers and devops engineers.We are hiring golang developers and devops engineers. 212121
Thank youThank youThank you
Dan TranDan TranDan Tran
@trongdan_tran@trongdan_tran@trongdan_tran(http://twitter.com/trongdan_tran)(http://twitter.com/trongdan_tran)(http://twitter.com/trongdan_tran)
Senior Software Developer atSenior Software Developer atSenior Software Developer at OpensimsimOpensimsimOpensimsim(https://opensimsim.com/)(https://opensimsim.com/)(https://opensimsim.com/)
How to not write a boring test in Golang

More Related Content

What's hot

Alexey Tsoy Meta Programming in C++ 16.11.17
Alexey Tsoy Meta Programming in C++ 16.11.17Alexey Tsoy Meta Programming in C++ 16.11.17
Alexey Tsoy Meta Programming in C++ 16.11.17
LogeekNightUkraine
 
Writing good std::future<c++>
Writing good std::future<c++>Writing good std::future<c++>
Writing good std::future<c++>
Anton Bikineev
 
Антон Бикинеев, Writing good std::future< C++ >
Антон Бикинеев, Writing good std::future< C++ >Антон Бикинеев, Writing good std::future< C++ >
Антон Бикинеев, Writing good std::future< C++ >
Sergey Platonov
 
How to Clone Flappy Bird in Swift
How to Clone Flappy Bird in SwiftHow to Clone Flappy Bird in Swift
How to Clone Flappy Bird in Swift
Giordano Scalzo
 
Clang tidy
Clang tidyClang tidy
Clang tidy
Yury Yafimachau
 
The Ring programming language version 1.5.3 book - Part 91 of 184
The Ring programming language version 1.5.3 book - Part 91 of 184The Ring programming language version 1.5.3 book - Part 91 of 184
The Ring programming language version 1.5.3 book - Part 91 of 184
Mahmoud Samir Fayed
 
Алексей Кутумов, Coroutines everywhere
Алексей Кутумов, Coroutines everywhereАлексей Кутумов, Coroutines everywhere
Алексей Кутумов, Coroutines everywhere
Sergey Platonov
 
PVS-Studio in 2021 - Error Examples
PVS-Studio in 2021 - Error ExamplesPVS-Studio in 2021 - Error Examples
PVS-Studio in 2021 - Error Examples
Andrey Karpov
 
Welcome to Modern C++
Welcome to Modern C++Welcome to Modern C++
Welcome to Modern C++
Seok-joon Yun
 
Pro typescript.ch03.Object Orientation in TypeScript
Pro typescript.ch03.Object Orientation in TypeScriptPro typescript.ch03.Object Orientation in TypeScript
Pro typescript.ch03.Object Orientation in TypeScript
Seok-joon Yun
 
How Data Flow analysis works in a static code analyzer
How Data Flow analysis works in a static code analyzerHow Data Flow analysis works in a static code analyzer
How Data Flow analysis works in a static code analyzer
Andrey Karpov
 
ITGM #9 - Коварный CodeType, или от segfault'а к работающему коду
ITGM #9 - Коварный CodeType, или от segfault'а к работающему кодуITGM #9 - Коварный CodeType, или от segfault'а к работающему коду
ITGM #9 - Коварный CodeType, или от segfault'а к работающему коду
delimitry
 
Lecture 4
Lecture 4Lecture 4
Lecture 4
sajidpk92
 
Коварный code type ITGM #9
Коварный code type ITGM #9Коварный code type ITGM #9
Коварный code type ITGM #9
Andrey Zakharevich
 
PVS-Studio is there to help CERN: analysis of Geant4 project
PVS-Studio is there to help CERN: analysis of Geant4 projectPVS-Studio is there to help CERN: analysis of Geant4 project
PVS-Studio is there to help CERN: analysis of Geant4 project
PVS-Studio
 
Network lab manual
Network lab manualNetwork lab manual
Network lab manual
Prabhu D
 
Constructor,destructors cpp
Constructor,destructors cppConstructor,destructors cpp
Constructor,destructors cpp
रमन सनौरिया
 
Антон Бикинеев, Reflection in C++Next
Антон Бикинеев,  Reflection in C++NextАнтон Бикинеев,  Reflection in C++Next
Антон Бикинеев, Reflection in C++Next
Sergey Platonov
 
Python Yield
Python YieldPython Yield
Python Yield
yangjuven
 
Swift 2
Swift 2Swift 2
Swift 2
Jens Ravens
 

What's hot (20)

Alexey Tsoy Meta Programming in C++ 16.11.17
Alexey Tsoy Meta Programming in C++ 16.11.17Alexey Tsoy Meta Programming in C++ 16.11.17
Alexey Tsoy Meta Programming in C++ 16.11.17
 
Writing good std::future<c++>
Writing good std::future<c++>Writing good std::future<c++>
Writing good std::future<c++>
 
Антон Бикинеев, Writing good std::future< C++ >
Антон Бикинеев, Writing good std::future< C++ >Антон Бикинеев, Writing good std::future< C++ >
Антон Бикинеев, Writing good std::future< C++ >
 
How to Clone Flappy Bird in Swift
How to Clone Flappy Bird in SwiftHow to Clone Flappy Bird in Swift
How to Clone Flappy Bird in Swift
 
Clang tidy
Clang tidyClang tidy
Clang tidy
 
The Ring programming language version 1.5.3 book - Part 91 of 184
The Ring programming language version 1.5.3 book - Part 91 of 184The Ring programming language version 1.5.3 book - Part 91 of 184
The Ring programming language version 1.5.3 book - Part 91 of 184
 
Алексей Кутумов, Coroutines everywhere
Алексей Кутумов, Coroutines everywhereАлексей Кутумов, Coroutines everywhere
Алексей Кутумов, Coroutines everywhere
 
PVS-Studio in 2021 - Error Examples
PVS-Studio in 2021 - Error ExamplesPVS-Studio in 2021 - Error Examples
PVS-Studio in 2021 - Error Examples
 
Welcome to Modern C++
Welcome to Modern C++Welcome to Modern C++
Welcome to Modern C++
 
Pro typescript.ch03.Object Orientation in TypeScript
Pro typescript.ch03.Object Orientation in TypeScriptPro typescript.ch03.Object Orientation in TypeScript
Pro typescript.ch03.Object Orientation in TypeScript
 
How Data Flow analysis works in a static code analyzer
How Data Flow analysis works in a static code analyzerHow Data Flow analysis works in a static code analyzer
How Data Flow analysis works in a static code analyzer
 
ITGM #9 - Коварный CodeType, или от segfault'а к работающему коду
ITGM #9 - Коварный CodeType, или от segfault'а к работающему кодуITGM #9 - Коварный CodeType, или от segfault'а к работающему коду
ITGM #9 - Коварный CodeType, или от segfault'а к работающему коду
 
Lecture 4
Lecture 4Lecture 4
Lecture 4
 
Коварный code type ITGM #9
Коварный code type ITGM #9Коварный code type ITGM #9
Коварный code type ITGM #9
 
PVS-Studio is there to help CERN: analysis of Geant4 project
PVS-Studio is there to help CERN: analysis of Geant4 projectPVS-Studio is there to help CERN: analysis of Geant4 project
PVS-Studio is there to help CERN: analysis of Geant4 project
 
Network lab manual
Network lab manualNetwork lab manual
Network lab manual
 
Constructor,destructors cpp
Constructor,destructors cppConstructor,destructors cpp
Constructor,destructors cpp
 
Антон Бикинеев, Reflection in C++Next
Антон Бикинеев,  Reflection in C++NextАнтон Бикинеев,  Reflection in C++Next
Антон Бикинеев, Reflection in C++Next
 
Python Yield
Python YieldPython Yield
Python Yield
 
Swift 2
Swift 2Swift 2
Swift 2
 

Similar to How to not write a boring test in Golang

Is your C# optimized
Is your C# optimizedIs your C# optimized
Is your C# optimized
Woody Pewitt
 
Kpi driven-java-development-fn conf
Kpi driven-java-development-fn confKpi driven-java-development-fn conf
Kpi driven-java-development-fn conf
Anirban Bhattacharjee
 
Pooya Khaloo Presentation on IWMC 2015
Pooya Khaloo Presentation on IWMC 2015Pooya Khaloo Presentation on IWMC 2015
Pooya Khaloo Presentation on IWMC 2015
Iran Entrepreneurship Association
 
A swift introduction to Swift
A swift introduction to SwiftA swift introduction to Swift
A swift introduction to Swift
Giordano Scalzo
 
Groovy
GroovyGroovy
Groovy
Zen Urban
 
Let's refine your Scala Code
Let's refine your Scala CodeLet's refine your Scala Code
Let's refine your Scala Code
Tech Triveni
 
Java Script Workshop
Java Script WorkshopJava Script Workshop
Java Script Workshop
Dmitry Baranovskiy
 
Groovy grails types, operators, objects
Groovy grails types, operators, objectsGroovy grails types, operators, objects
Groovy grails types, operators, objects
Husain Dalal
 
Go testdeep
Go testdeepGo testdeep
Go testdeep
Maxime Soulé
 
java compilerCompiler1.javajava compilerCompiler1.javaimport.docx
java compilerCompiler1.javajava compilerCompiler1.javaimport.docxjava compilerCompiler1.javajava compilerCompiler1.javaimport.docx
java compilerCompiler1.javajava compilerCompiler1.javaimport.docx
priestmanmable
 
Best Bugs from Games: Fellow Programmers' Mistakes
Best Bugs from Games: Fellow Programmers' MistakesBest Bugs from Games: Fellow Programmers' Mistakes
Best Bugs from Games: Fellow Programmers' Mistakes
Andrey Karpov
 
An introduction to Google test framework
An introduction to Google test frameworkAn introduction to Google test framework
An introduction to Google test framework
Abner Chih Yi Huang
 
Privet Kotlin (Windy City DevFest)
Privet Kotlin (Windy City DevFest)Privet Kotlin (Windy City DevFest)
Privet Kotlin (Windy City DevFest)
Cody Engel
 
From android/java to swift (3)
From android/java to swift (3)From android/java to swift (3)
From android/java to swift (3)
allanh0526
 
Kamil witecki asynchronous, yet readable, code
Kamil witecki asynchronous, yet readable, codeKamil witecki asynchronous, yet readable, code
Kamil witecki asynchronous, yet readable, code
Kamil Witecki
 
Whats new in_csharp4
Whats new in_csharp4Whats new in_csharp4
Whats new in_csharp4
Abed Bukhari
 
Kotlin, smarter development for the jvm
Kotlin, smarter development for the jvmKotlin, smarter development for the jvm
Kotlin, smarter development for the jvm
Arnaud Giuliani
 
Java 5 Features
Java 5 FeaturesJava 5 Features
Java 5 Features
sholavanalli
 
How do you create a programming language for the JVM?
How do you create a programming language for the JVM?How do you create a programming language for the JVM?
How do you create a programming language for the JVM?
Federico Tomassetti
 
Exploring Koltin on Android
Exploring Koltin on AndroidExploring Koltin on Android
Exploring Koltin on Android
Deepanshu Madan
 

Similar to How to not write a boring test in Golang (20)

Is your C# optimized
Is your C# optimizedIs your C# optimized
Is your C# optimized
 
Kpi driven-java-development-fn conf
Kpi driven-java-development-fn confKpi driven-java-development-fn conf
Kpi driven-java-development-fn conf
 
Pooya Khaloo Presentation on IWMC 2015
Pooya Khaloo Presentation on IWMC 2015Pooya Khaloo Presentation on IWMC 2015
Pooya Khaloo Presentation on IWMC 2015
 
A swift introduction to Swift
A swift introduction to SwiftA swift introduction to Swift
A swift introduction to Swift
 
Groovy
GroovyGroovy
Groovy
 
Let's refine your Scala Code
Let's refine your Scala CodeLet's refine your Scala Code
Let's refine your Scala Code
 
Java Script Workshop
Java Script WorkshopJava Script Workshop
Java Script Workshop
 
Groovy grails types, operators, objects
Groovy grails types, operators, objectsGroovy grails types, operators, objects
Groovy grails types, operators, objects
 
Go testdeep
Go testdeepGo testdeep
Go testdeep
 
java compilerCompiler1.javajava compilerCompiler1.javaimport.docx
java compilerCompiler1.javajava compilerCompiler1.javaimport.docxjava compilerCompiler1.javajava compilerCompiler1.javaimport.docx
java compilerCompiler1.javajava compilerCompiler1.javaimport.docx
 
Best Bugs from Games: Fellow Programmers' Mistakes
Best Bugs from Games: Fellow Programmers' MistakesBest Bugs from Games: Fellow Programmers' Mistakes
Best Bugs from Games: Fellow Programmers' Mistakes
 
An introduction to Google test framework
An introduction to Google test frameworkAn introduction to Google test framework
An introduction to Google test framework
 
Privet Kotlin (Windy City DevFest)
Privet Kotlin (Windy City DevFest)Privet Kotlin (Windy City DevFest)
Privet Kotlin (Windy City DevFest)
 
From android/java to swift (3)
From android/java to swift (3)From android/java to swift (3)
From android/java to swift (3)
 
Kamil witecki asynchronous, yet readable, code
Kamil witecki asynchronous, yet readable, codeKamil witecki asynchronous, yet readable, code
Kamil witecki asynchronous, yet readable, code
 
Whats new in_csharp4
Whats new in_csharp4Whats new in_csharp4
Whats new in_csharp4
 
Kotlin, smarter development for the jvm
Kotlin, smarter development for the jvmKotlin, smarter development for the jvm
Kotlin, smarter development for the jvm
 
Java 5 Features
Java 5 FeaturesJava 5 Features
Java 5 Features
 
How do you create a programming language for the JVM?
How do you create a programming language for the JVM?How do you create a programming language for the JVM?
How do you create a programming language for the JVM?
 
Exploring Koltin on Android
Exploring Koltin on AndroidExploring Koltin on Android
Exploring Koltin on Android
 

Recently uploaded

Introduction of Cybersecurity with OSS at Code Europe 2024
Introduction of Cybersecurity with OSS  at Code Europe 2024Introduction of Cybersecurity with OSS  at Code Europe 2024
Introduction of Cybersecurity with OSS at Code Europe 2024
Hiroshi SHIBATA
 
Taking AI to the Next Level in Manufacturing.pdf
Taking AI to the Next Level in Manufacturing.pdfTaking AI to the Next Level in Manufacturing.pdf
Taking AI to the Next Level in Manufacturing.pdf
ssuserfac0301
 
Monitoring and Managing Anomaly Detection on OpenShift.pdf
Monitoring and Managing Anomaly Detection on OpenShift.pdfMonitoring and Managing Anomaly Detection on OpenShift.pdf
Monitoring and Managing Anomaly Detection on OpenShift.pdf
Tosin Akinosho
 
HCL Notes and Domino License Cost Reduction in the World of DLAU
HCL Notes and Domino License Cost Reduction in the World of DLAUHCL Notes and Domino License Cost Reduction in the World of DLAU
HCL Notes and Domino License Cost Reduction in the World of DLAU
panagenda
 
Digital Banking in the Cloud: How Citizens Bank Unlocked Their Mainframe
Digital Banking in the Cloud: How Citizens Bank Unlocked Their MainframeDigital Banking in the Cloud: How Citizens Bank Unlocked Their Mainframe
Digital Banking in the Cloud: How Citizens Bank Unlocked Their Mainframe
Precisely
 
A Comprehensive Guide to DeFi Development Services in 2024
A Comprehensive Guide to DeFi Development Services in 2024A Comprehensive Guide to DeFi Development Services in 2024
A Comprehensive Guide to DeFi Development Services in 2024
Intelisync
 
zkStudyClub - LatticeFold: A Lattice-based Folding Scheme and its Application...
zkStudyClub - LatticeFold: A Lattice-based Folding Scheme and its Application...zkStudyClub - LatticeFold: A Lattice-based Folding Scheme and its Application...
zkStudyClub - LatticeFold: A Lattice-based Folding Scheme and its Application...
Alex Pruden
 
dbms calicut university B. sc Cs 4th sem.pdf
dbms  calicut university B. sc Cs 4th sem.pdfdbms  calicut university B. sc Cs 4th sem.pdf
dbms calicut university B. sc Cs 4th sem.pdf
Shinana2
 
Let's Integrate MuleSoft RPA, COMPOSER, APM with AWS IDP along with Slack
Let's Integrate MuleSoft RPA, COMPOSER, APM with AWS IDP along with SlackLet's Integrate MuleSoft RPA, COMPOSER, APM with AWS IDP along with Slack
Let's Integrate MuleSoft RPA, COMPOSER, APM with AWS IDP along with Slack
shyamraj55
 
How to Interpret Trends in the Kalyan Rajdhani Mix Chart.pdf
How to Interpret Trends in the Kalyan Rajdhani Mix Chart.pdfHow to Interpret Trends in the Kalyan Rajdhani Mix Chart.pdf
How to Interpret Trends in the Kalyan Rajdhani Mix Chart.pdf
Chart Kalyan
 
Main news related to the CCS TSI 2023 (2023/1695)
Main news related to the CCS TSI 2023 (2023/1695)Main news related to the CCS TSI 2023 (2023/1695)
Main news related to the CCS TSI 2023 (2023/1695)
Jakub Marek
 
Skybuffer SAM4U tool for SAP license adoption
Skybuffer SAM4U tool for SAP license adoptionSkybuffer SAM4U tool for SAP license adoption
Skybuffer SAM4U tool for SAP license adoption
Tatiana Kojar
 
FREE A4 Cyber Security Awareness Posters-Social Engineering part 3
FREE A4 Cyber Security Awareness  Posters-Social Engineering part 3FREE A4 Cyber Security Awareness  Posters-Social Engineering part 3
FREE A4 Cyber Security Awareness Posters-Social Engineering part 3
Data Hops
 
SAP S/4 HANA sourcing and procurement to Public cloud
SAP S/4 HANA sourcing and procurement to Public cloudSAP S/4 HANA sourcing and procurement to Public cloud
SAP S/4 HANA sourcing and procurement to Public cloud
maazsz111
 
5th LF Energy Power Grid Model Meet-up Slides
5th LF Energy Power Grid Model Meet-up Slides5th LF Energy Power Grid Model Meet-up Slides
5th LF Energy Power Grid Model Meet-up Slides
DanBrown980551
 
Programming Foundation Models with DSPy - Meetup Slides
Programming Foundation Models with DSPy - Meetup SlidesProgramming Foundation Models with DSPy - Meetup Slides
Programming Foundation Models with DSPy - Meetup Slides
Zilliz
 
JavaLand 2024: Application Development Green Masterplan
JavaLand 2024: Application Development Green MasterplanJavaLand 2024: Application Development Green Masterplan
JavaLand 2024: Application Development Green Masterplan
Miro Wengner
 
Energy Efficient Video Encoding for Cloud and Edge Computing Instances
Energy Efficient Video Encoding for Cloud and Edge Computing InstancesEnergy Efficient Video Encoding for Cloud and Edge Computing Instances
Energy Efficient Video Encoding for Cloud and Edge Computing Instances
Alpen-Adria-Universität
 
Astute Business Solutions | Oracle Cloud Partner |
Astute Business Solutions | Oracle Cloud Partner |Astute Business Solutions | Oracle Cloud Partner |
Astute Business Solutions | Oracle Cloud Partner |
AstuteBusiness
 
Deep Dive: AI-Powered Marketing to Get More Leads and Customers with HyperGro...
Deep Dive: AI-Powered Marketing to Get More Leads and Customers with HyperGro...Deep Dive: AI-Powered Marketing to Get More Leads and Customers with HyperGro...
Deep Dive: AI-Powered Marketing to Get More Leads and Customers with HyperGro...
saastr
 

Recently uploaded (20)

Introduction of Cybersecurity with OSS at Code Europe 2024
Introduction of Cybersecurity with OSS  at Code Europe 2024Introduction of Cybersecurity with OSS  at Code Europe 2024
Introduction of Cybersecurity with OSS at Code Europe 2024
 
Taking AI to the Next Level in Manufacturing.pdf
Taking AI to the Next Level in Manufacturing.pdfTaking AI to the Next Level in Manufacturing.pdf
Taking AI to the Next Level in Manufacturing.pdf
 
Monitoring and Managing Anomaly Detection on OpenShift.pdf
Monitoring and Managing Anomaly Detection on OpenShift.pdfMonitoring and Managing Anomaly Detection on OpenShift.pdf
Monitoring and Managing Anomaly Detection on OpenShift.pdf
 
HCL Notes and Domino License Cost Reduction in the World of DLAU
HCL Notes and Domino License Cost Reduction in the World of DLAUHCL Notes and Domino License Cost Reduction in the World of DLAU
HCL Notes and Domino License Cost Reduction in the World of DLAU
 
Digital Banking in the Cloud: How Citizens Bank Unlocked Their Mainframe
Digital Banking in the Cloud: How Citizens Bank Unlocked Their MainframeDigital Banking in the Cloud: How Citizens Bank Unlocked Their Mainframe
Digital Banking in the Cloud: How Citizens Bank Unlocked Their Mainframe
 
A Comprehensive Guide to DeFi Development Services in 2024
A Comprehensive Guide to DeFi Development Services in 2024A Comprehensive Guide to DeFi Development Services in 2024
A Comprehensive Guide to DeFi Development Services in 2024
 
zkStudyClub - LatticeFold: A Lattice-based Folding Scheme and its Application...
zkStudyClub - LatticeFold: A Lattice-based Folding Scheme and its Application...zkStudyClub - LatticeFold: A Lattice-based Folding Scheme and its Application...
zkStudyClub - LatticeFold: A Lattice-based Folding Scheme and its Application...
 
dbms calicut university B. sc Cs 4th sem.pdf
dbms  calicut university B. sc Cs 4th sem.pdfdbms  calicut university B. sc Cs 4th sem.pdf
dbms calicut university B. sc Cs 4th sem.pdf
 
Let's Integrate MuleSoft RPA, COMPOSER, APM with AWS IDP along with Slack
Let's Integrate MuleSoft RPA, COMPOSER, APM with AWS IDP along with SlackLet's Integrate MuleSoft RPA, COMPOSER, APM with AWS IDP along with Slack
Let's Integrate MuleSoft RPA, COMPOSER, APM with AWS IDP along with Slack
 
How to Interpret Trends in the Kalyan Rajdhani Mix Chart.pdf
How to Interpret Trends in the Kalyan Rajdhani Mix Chart.pdfHow to Interpret Trends in the Kalyan Rajdhani Mix Chart.pdf
How to Interpret Trends in the Kalyan Rajdhani Mix Chart.pdf
 
Main news related to the CCS TSI 2023 (2023/1695)
Main news related to the CCS TSI 2023 (2023/1695)Main news related to the CCS TSI 2023 (2023/1695)
Main news related to the CCS TSI 2023 (2023/1695)
 
Skybuffer SAM4U tool for SAP license adoption
Skybuffer SAM4U tool for SAP license adoptionSkybuffer SAM4U tool for SAP license adoption
Skybuffer SAM4U tool for SAP license adoption
 
FREE A4 Cyber Security Awareness Posters-Social Engineering part 3
FREE A4 Cyber Security Awareness  Posters-Social Engineering part 3FREE A4 Cyber Security Awareness  Posters-Social Engineering part 3
FREE A4 Cyber Security Awareness Posters-Social Engineering part 3
 
SAP S/4 HANA sourcing and procurement to Public cloud
SAP S/4 HANA sourcing and procurement to Public cloudSAP S/4 HANA sourcing and procurement to Public cloud
SAP S/4 HANA sourcing and procurement to Public cloud
 
5th LF Energy Power Grid Model Meet-up Slides
5th LF Energy Power Grid Model Meet-up Slides5th LF Energy Power Grid Model Meet-up Slides
5th LF Energy Power Grid Model Meet-up Slides
 
Programming Foundation Models with DSPy - Meetup Slides
Programming Foundation Models with DSPy - Meetup SlidesProgramming Foundation Models with DSPy - Meetup Slides
Programming Foundation Models with DSPy - Meetup Slides
 
JavaLand 2024: Application Development Green Masterplan
JavaLand 2024: Application Development Green MasterplanJavaLand 2024: Application Development Green Masterplan
JavaLand 2024: Application Development Green Masterplan
 
Energy Efficient Video Encoding for Cloud and Edge Computing Instances
Energy Efficient Video Encoding for Cloud and Edge Computing InstancesEnergy Efficient Video Encoding for Cloud and Edge Computing Instances
Energy Efficient Video Encoding for Cloud and Edge Computing Instances
 
Astute Business Solutions | Oracle Cloud Partner |
Astute Business Solutions | Oracle Cloud Partner |Astute Business Solutions | Oracle Cloud Partner |
Astute Business Solutions | Oracle Cloud Partner |
 
Deep Dive: AI-Powered Marketing to Get More Leads and Customers with HyperGro...
Deep Dive: AI-Powered Marketing to Get More Leads and Customers with HyperGro...Deep Dive: AI-Powered Marketing to Get More Leads and Customers with HyperGro...
Deep Dive: AI-Powered Marketing to Get More Leads and Customers with HyperGro...
 

How to not write a boring test in Golang

  • 1. How to not write a boring testHow to not write a boring testHow to not write a boring test Dan TranDan TranDan Tran
  • 2. Development and TestingDevelopment and TestingDevelopment and Testing 222
  • 3. Test Driven DevelopmentTest Driven DevelopmentTest Driven Development Write testWrite testWrite test Write codeWrite codeWrite code 333
  • 4. Unit testingUnit testingUnit testing Table-driven testTable-driven testTable-driven test StubStubStub MockingMockingMocking 444
  • 5. Table-driven testTable-driven testTable-driven test // Gcd returns the greatest common divisor of 2 numbers.// Gcd returns the greatest common divisor of 2 numbers.// Gcd returns the greatest common divisor of 2 numbers. func Gcd(a, b int) int {func Gcd(a, b int) int {func Gcd(a, b int) int { if a%2 == 0 {if a%2 == 0 {if a%2 == 0 { if b%2 == 0 {if b%2 == 0 {if b%2 == 0 { return 2 * Gcd(a/2, b/2)return 2 * Gcd(a/2, b/2)return 2 * Gcd(a/2, b/2) }}} return Gcd(a/2, b)return Gcd(a/2, b)return Gcd(a/2, b) }}} if b%2 == 0 {if b%2 == 0 {if b%2 == 0 { return Gcd(a, b/2)return Gcd(a, b/2)return Gcd(a, b/2) }}} if a == b {if a == b {if a == b { return areturn areturn a }}} if a > b {if a > b {if a > b { return Gcd(a-b, b)return Gcd(a-b, b)return Gcd(a-b, b) }}} return Gcd(b-a, a)return Gcd(b-a, a)return Gcd(b-a, a) }}} Run 555
  • 6. Table-driven test (2)Table-driven test (2)Table-driven test (2) func TestGcd(t *testing.T) {func TestGcd(t *testing.T) {func TestGcd(t *testing.T) { tests := []struct {tests := []struct {tests := []struct { name stringname stringname string args argsargs argsargs args want intwant intwant int }{}{}{ {{{ "Test two different numbers","Test two different numbers","Test two different numbers", args{a: 4, b: 6},args{a: 4, b: 6},args{a: 4, b: 6}, 2,2,2, },},}, {{{ "Test two identical numbers","Test two identical numbers","Test two identical numbers", args{a: 5, b: 5},args{a: 5, b: 5},args{a: 5, b: 5}, 5,5,5, },},}, }}} for _, tt := range tests {for _, tt := range tests {for _, tt := range tests { tt := tttt := tttt := tt t.Run(tt.name, func(t *testing.T) {t.Run(tt.name, func(t *testing.T) {t.Run(tt.name, func(t *testing.T) { if got := Gcd(tt.args.a, tt.args.b); got != tt.want {if got := Gcd(tt.args.a, tt.args.b); got != tt.want {if got := Gcd(tt.args.a, tt.args.b); got != tt.want { t.Errorf("Gcd() = %v, want %v", got, tt.want)t.Errorf("Gcd() = %v, want %v", got, tt.want)t.Errorf("Gcd() = %v, want %v", got, tt.want) }}} })})}) }}} }}} Run
  • 7. 666
  • 8. ExampleExampleExample package stripepackage stripepackage stripe // Client ...// Client ...// Client ... // go:generate mockery -name=Client -case=underscore// go:generate mockery -name=Client -case=underscore// go:generate mockery -name=Client -case=underscore type Client interface {type Client interface {type Client interface { GetBalance(customerID string, currency string) (int64, error)GetBalance(customerID string, currency string) (int64, error)GetBalance(customerID string, currency string) (int64, error) Charge(customerID string, amount int64, currency string, desc string) errorCharge(customerID string, amount int64, currency string, desc string) errorCharge(customerID string, amount int64, currency string, desc string) error }}} Run 777
  • 9. Stub (1)Stub (1)Stub (1) Create an implementation which has the same set of functions as the mainCreate an implementation which has the same set of functions as the mainCreate an implementation which has the same set of functions as the main object/class.object/class.object/class. type stubClient struct{}type stubClient struct{}type stubClient struct{} // GetBalance ...// GetBalance ...// GetBalance ... func (c *stubClient) GetBalance(customerID string, currency string) (int64, error) {func (c *stubClient) GetBalance(customerID string, currency string) (int64, error) {func (c *stubClient) GetBalance(customerID string, currency string) (int64, error) { return 100, nilreturn 100, nilreturn 100, nil }}} // Charge ...// Charge ...// Charge ... func (c *stubClient) Charge(customerID string, amount int64, currency string, desc string) errorfunc (c *stubClient) Charge(customerID string, amount int64, currency string, desc string) errorfunc (c *stubClient) Charge(customerID string, amount int64, currency string, desc string) error return nilreturn nilreturn nil }}} // TestGetBalanceHappyPath// TestGetBalanceHappyPath// TestGetBalanceHappyPath Run 888
  • 10. Stub (2)Stub (2)Stub (2) func TestGetBalanceHappyPath(t *testing.T) {func TestGetBalanceHappyPath(t *testing.T) {func TestGetBalanceHappyPath(t *testing.T) { stripeClient = &stubClient{}stripeClient = &stubClient{}stripeClient = &stubClient{} t.Run("Happy path", func(t *testing.T) {t.Run("Happy path", func(t *testing.T) {t.Run("Happy path", func(t *testing.T) { got, err := GetBalance(existingCustomerID)got, err := GetBalance(existingCustomerID)got, err := GetBalance(existingCustomerID) if err != nil {if err != nil {if err != nil { t.Errorf("GetBalance() error = %v, wantErr %v", err, nil)t.Errorf("GetBalance() error = %v, wantErr %v", err, nil)t.Errorf("GetBalance() error = %v, wantErr %v", err, nil) returnreturnreturn }}} if got != 100 {if got != 100 {if got != 100 { t.Errorf("GetBalance() = %v, want %v", got, 100)t.Errorf("GetBalance() = %v, want %v", got, 100)t.Errorf("GetBalance() = %v, want %v", got, 100) }}} })})}) }}} Run 999
  • 11. Generate mockGenerate mockGenerate mock Using mockery: go get github.com/vektra/mockery/...Using mockery: go get github.com/vektra/mockery/...Using mockery: go get github.com/vektra/mockery/... mockery -name=Client -case=underscoremockery -name=Client -case=underscoremockery -name=Client -case=underscore In test leIn test leIn test le m := mocks.Client{}m := mocks.Client{}m := mocks.Client{} m.On("GetBalance", mock.AnythingOfType("string"), mock.AnythingOfType("string")).m.On("GetBalance", mock.AnythingOfType("string"), mock.AnythingOfType("string")).m.On("GetBalance", mock.AnythingOfType("string"), mock.AnythingOfType("string")). Return(int64(100), nil).Once()Return(int64(100), nil).Once()Return(int64(100), nil).Once() 101010
  • 12. MockingMockingMocking for _, scenario := range tests {for _, scenario := range tests {for _, scenario := range tests { scenario := scenarioscenario := scenarioscenario := scenario t.Run(scenario.name, func(t *testing.T) {t.Run(scenario.name, func(t *testing.T) {t.Run(scenario.name, func(t *testing.T) { m := &mocks.Client{}m := &mocks.Client{}m := &mocks.Client{} scenario.configureMock(m)scenario.configureMock(m)scenario.configureMock(m) stripeClient = mstripeClient = mstripeClient = m got, err := GetBalance(scenario.userID)got, err := GetBalance(scenario.userID)got, err := GetBalance(scenario.userID) if (err != nil) != scenario.wantErr {if (err != nil) != scenario.wantErr {if (err != nil) != scenario.wantErr { t.Errorf("GetBalance() error = %v, wantErr %v", err, scenario.wantErr)t.Errorf("GetBalance() error = %v, wantErr %v", err, scenario.wantErr)t.Errorf("GetBalance() error = %v, wantErr %v", err, scenario.wantErr) returnreturnreturn }}} if got != scenario.want {if got != scenario.want {if got != scenario.want { t.Errorf("GetBalance() = %v, want %v", got, scenario.want)t.Errorf("GetBalance() = %v, want %v", got, scenario.want)t.Errorf("GetBalance() = %v, want %v", got, scenario.want) returnreturnreturn }}} })})}) }}} }}} Run 111111
  • 14. Monkey patchingMonkey patchingMonkey patching In api.goIn api.goIn api.go In api_test.goIn api_test.goIn api_test.go originalFn := getBalanceoriginalFn := getBalanceoriginalFn := getBalance getBalance = func(string, string)(int, error){...}getBalance = func(string, string)(int, error){...}getBalance = func(string, string)(int, error){...} defer func() {defer func() {defer func() { getBalance = originalFngetBalance = originalFngetBalance = originalFn }()}()}() var getBalance = stripeClient.GetBalancevar getBalance = stripeClient.GetBalancevar getBalance = stripeClient.GetBalance // GetBalance2 ...// GetBalance2 ...// GetBalance2 ... func GetBalance2(userID string) (int64, error) {func GetBalance2(userID string) (int64, error) {func GetBalance2(userID string) (int64, error) { currency := loadCurrencyByUser(userID)currency := loadCurrencyByUser(userID)currency := loadCurrencyByUser(userID) bal, err := getBalance(userID, currency)bal, err := getBalance(userID, currency)bal, err := getBalance(userID, currency) if err != nil {if err != nil {if err != nil { return 0, errreturn 0, errreturn 0, err }}} return bal, nilreturn bal, nilreturn bal, nil }}} Run 131313
  • 15. Behaviour Driven DevelopmentBehaviour Driven DevelopmentBehaviour Driven Development 141414
  • 16. Cucumber for golangCucumber for golangCucumber for golang Feature: get versionFeature: get versionFeature: get version In order to know godog versionIn order to know godog versionIn order to know godog version As an API userAs an API userAs an API user I need to be able to request versionI need to be able to request versionI need to be able to request version Scenario: does not allow POST methodScenario: does not allow POST methodScenario: does not allow POST method When I send "POST" request to "/version"When I send "POST" request to "/version"When I send "POST" request to "/version" Then the response code should be 405Then the response code should be 405Then the response code should be 405 And the response should match json:And the response should match json:And the response should match json: """"""""" {{{ "error": "Method not allowed""error": "Method not allowed""error": "Method not allowed" }}} """"""""" Scenario: should get version numberScenario: should get version numberScenario: should get version number When I send "GET" request to "/version"When I send "GET" request to "/version"When I send "GET" request to "/version" Then the response code should be 200Then the response code should be 200Then the response code should be 200 And the response should match json:And the response should match json:And the response should match json: """"""""" {{{ "version": "v0.7.13""version": "v0.7.13""version": "v0.7.13" }}} """"""""" Run 151515
  • 17. Cucumber style - GodogCucumber style - GodogCucumber style - Godog func FeatureContext(s *godog.Suite) {func FeatureContext(s *godog.Suite) {func FeatureContext(s *godog.Suite) { api := &apiFeature{}api := &apiFeature{}api := &apiFeature{} s.BeforeScenario(api.resetResponse)s.BeforeScenario(api.resetResponse)s.BeforeScenario(api.resetResponse) s.Step(`^I send "(GET|POST|PUT|DELETE)" request to "([^"]*)"$`, api.iSendrequestTo)s.Step(`^I send "(GET|POST|PUT|DELETE)" request to "([^"]*)"$`, api.iSendrequestTo)s.Step(`^I send "(GET|POST|PUT|DELETE)" request to "([^"]*)"$`, api.iSendrequestTo) s.Step(`^the response code should be (d+)$`, api.theResponseCodeShouldBe)s.Step(`^the response code should be (d+)$`, api.theResponseCodeShouldBe)s.Step(`^the response code should be (d+)$`, api.theResponseCodeShouldBe) s.Step(`^the response should match json:$`, api.theResponseShouldMatchJSON)s.Step(`^the response should match json:$`, api.theResponseShouldMatchJSON)s.Step(`^the response should match json:$`, api.theResponseShouldMatchJSON) }}} Run 161616
  • 18. Other TipsOther TipsOther Tips Make a habit: de ne a package with interface and mockMake a habit: de ne a package with interface and mockMake a habit: de ne a package with interface and mock What I should test: functions or behaviours. Write happy path case rst.What I should test: functions or behaviours. Write happy path case rst.What I should test: functions or behaviours. Write happy path case rst. Consider writting tests for public funcs than private func.Consider writting tests for public funcs than private func.Consider writting tests for public funcs than private func. Create di erent build tag for unit and integration testsCreate di erent build tag for unit and integration testsCreate di erent build tag for unit and integration tests // +build unit// +build unit// +build unit // +build integration// +build integration// +build integration Disabling cache with count=1Disabling cache with count=1Disabling cache with count=1 171717
  • 19. Test coverageTest coverageTest coverage go test -covermode=count -coverprofile=count.outgo test -covermode=count -coverprofile=count.outgo test -covermode=count -coverprofile=count.out go tool cover -func=count.outgo tool cover -func=count.outgo tool cover -func=count.out 181818
  • 20. Using git pre-commit hookUsing git pre-commit hookUsing git pre-commit hook Edit local changes in .git/hooks/pre-commitEdit local changes in .git/hooks/pre-commitEdit local changes in .git/hooks/pre-commit Trigger go lint/test before every commitTrigger go lint/test before every commitTrigger go lint/test before every commit #!/bin/sh#!/bin/sh#!/bin/sh # run go lint# run go lint# run go lint # run unit tests# run unit tests# run unit tests go test -count=1 -short ./...go test -count=1 -short ./...go test -count=1 -short ./... if [[ $? == 1 ]]; thenif [[ $? == 1 ]]; thenif [[ $? == 1 ]]; then printf "unit test failed"printf "unit test failed"printf "unit test failed" exit 1exit 1exit 1 fififi # run integration tests# run integration tests# run integration tests # go test --tags=integration ./...# go test --tags=integration ./...# go test --tags=integration ./... exit 0exit 0exit 0 Run 191919
  • 21. ResourcesResourcesResources Demo code:Demo code:Demo code: gitlab.com/aladine/testing-demogitlab.com/aladine/testing-demogitlab.com/aladine/testing-demo(https://gitlab.com/aladine/testing-demo)(https://gitlab.com/aladine/testing-demo)(https://gitlab.com/aladine/testing-demo) Hands-On Dependency Injection in Go, Corey Scott:Hands-On Dependency Injection in Go, Corey Scott:Hands-On Dependency Injection in Go, Corey Scott: EbookEbookEbook (https://subscription.packtpub.com/book/application_development/9781789132762)(https://subscription.packtpub.com/book/application_development/9781789132762)(https://subscription.packtpub.com/book/application_development/9781789132762) https://github.com/DATA-DOG/godoghttps://github.com/DATA-DOG/godoghttps://github.com/DATA-DOG/godog Arts icon byArts icon byArts icon by twitter.com/deniseyu21twitter.com/deniseyu21twitter.com/deniseyu21(https://twitter.com/deniseyu21)(https://twitter.com/deniseyu21)(https://twitter.com/deniseyu21) 202020
  • 22. Hiring - OpenSimSimHiring - OpenSimSimHiring - OpenSimSim We are hiring golang developers and devops engineers.We are hiring golang developers and devops engineers.We are hiring golang developers and devops engineers. 212121
  • 23. Thank youThank youThank you Dan TranDan TranDan Tran @trongdan_tran@trongdan_tran@trongdan_tran(http://twitter.com/trongdan_tran)(http://twitter.com/trongdan_tran)(http://twitter.com/trongdan_tran) Senior Software Developer atSenior Software Developer atSenior Software Developer at OpensimsimOpensimsimOpensimsim(https://opensimsim.com/)(https://opensimsim.com/)(https://opensimsim.com/)