tdd in go with ginkgo and gomega

33
TDD in Go with Ginkgo and Gomega Eddy Reyes - [email protected] http://www.tasqr.io

Upload: eddy-reyes

Post on 05-Jul-2015

294 views

Category:

Software


0 download

DESCRIPTION

An overview of TDD concepts, and an exploration of these concepts in Go using the Ginkgo and Gomega libraries.

TRANSCRIPT

Page 1: TDD in Go with Ginkgo and Gomega

TDD in Go with

Ginkgo and GomegaEddy Reyes - [email protected]

http://www.tasqr.io

Page 2: TDD in Go with Ginkgo and Gomega

About Me

Eddy Reyes. Cofounder of Tasqr

(www.tasqr.io)

● Automates application deployment /

configuration without code or config files.

Previously hacked lots of code at:

● IBM, Lifesize, Click Security

Page 3: TDD in Go with Ginkgo and Gomega

Overview of Presentation

● Explain what TDD iso Not necessarily proselytizing that you must do it.

o I dislike many TDD-related presentations, they don’t

match my experience, they don’t stress what’s

important IMO.

● What Tools Does Go Have to Help You Do

TDD?o Ginkgo and Gomega, many others

Page 4: TDD in Go with Ginkgo and Gomega

Further TDD Reading

Uncle Bob Martin● http://blog.8thlight.com/uncle-bob/archive.html

Kent Beck● Extreme Programming Explained (book)

Is TDD Dead? (video debates)● Kent Beck, Martin Fowler, DHH

● https://www.youtube.com/watch?v=z9quxZsLcfo (part 1)

Page 5: TDD in Go with Ginkgo and Gomega

What Isn’t TDD?

● NOT just having written tests

● NOT a way to feel like you’re going fast

● NOT the Silver Bullet that saves software

engineering

● NOT like Axe, which makes you irresistible

to the opposite sex :-)

Page 6: TDD in Go with Ginkgo and Gomega

What Is TDD?

● A process you follow when writing software.

● The process goes like this:o You write a test that defines an expected outcome in

your program.

o You watch the test fail.

o You change the program until your test passes.

● Adding tests to an already substantial

untested program isn’t necessarily TDD.

Page 7: TDD in Go with Ginkgo and Gomega

How Do You Do TDD?

● In order to effectively follow the TDD pattern,

you must first understand your design.o Tests are rigid. Learning is free-form.

o You learn by hacking and breaking things.

o Tests ensure things don’t break.

● TDD helps you SHIP, not LEARN

Page 8: TDD in Go with Ginkgo and Gomega

Learn vs. Ship

● Learning is free-form exploration.

Experiment, play, break things.o DO NOT SHIP THIS CODE!

o After you learn your design, step away from the

keyboard, and start anew.

● Shippingo Code ends up in a product used by customers.

o Quality is important!

Page 9: TDD in Go with Ginkgo and Gomega

Tests Are Design!

● If tests are comprehensive, they are a formal

specification of your program.

● Writing a test is expressing the design of

your program’s functionality.

● To truly serve as a spec, tests must be:o Clean, readable

o Follow some consistent notation

Page 10: TDD in Go with Ginkgo and Gomega

Test/Spec Notation

GIVEN

● Precondition 1...N

WHEN

● Call into your program

THEN

● Assert Condition 1...N

Page 11: TDD in Go with Ginkgo and Gomega

A Test Framework Needs...

● Clean, simple notation

● Clean setup/teardown of dependencieso Idempotent, independent tests!

● Clean separation from tested code

● Simple runner mechanism

● Good reportingo test results

o coverage maps

Page 12: TDD in Go with Ginkgo and Gomega

Testing in Go

● Test runnero go test

● Separates test code from producto *_test.go

● Comes with a test writing libraryo “testing” package

● Comes with coverage reporting toolso Beautiful HTML output

Page 13: TDD in Go with Ginkgo and Gomega

“testing” Package

● Allows your to write test functions.

● Nice benchmarking utility

● Tests are basically blocks of code :(func TestXXX(t *testing.T) {

// setup stuff

// call your code

if something.State != WhatIWant {

t.Fail()

}

}

Page 14: TDD in Go with Ginkgo and Gomega

Better Test-Writing Library

● go command has great tooling.

● Need a better notation for specifying tests.

Ginkgo and Gomega is a step forward!

… but not perfect!

Page 15: TDD in Go with Ginkgo and Gomega

Ginkgo and Gomega

● Ginkgoo Compatible with “go test” but also comes with its

own test runner

o BDD-style test notation

o Expressive structuring of tests

● Gomegao “Matching” library

o Allows for expressive assert-like statements

Page 16: TDD in Go with Ginkgo and Gomega

Ginkgo

● Tests expressed as English statementso In the BDD tradition of Cucumber, et. al.

var _ = Describe(“My component”, func() {

It(“Does something special”, func() {

MyFunction()

Expect(MyVar).To(Equal(10))

})

})

Page 17: TDD in Go with Ginkgo and Gomega

Gomega

● Assertion languageExpect(number).To(Equal(10))

Expect(value).To(BeNumerically(“<”, 20))

Expect(someMap).To(Equal(otherMap)) // does reflect.DeepEqual()

Expect(someMap).To(BeEquivalent(otherMap)) // allows different types

Expect(myPtr).ToNot(BeNil())

Expect(flag).To(BeFalse())

Expect(err).ToNot(HaveOccurred())

Expect(err).To(MatchError(“my error message”))

Page 18: TDD in Go with Ginkgo and Gomega

Ginkgo and Dependencies● setup/teardown:

var _ = Describe(“my config object”, func() {

var (

configObj *config.MyConfig

tempDir string

)

BeforeEach(func() {

var err error

tempDir, err = ioutil.TempDir(“”, “test-dir”)

Expect(err).ToNot(HaveOccurred())

configObj = config.NewConfig()

})

Page 19: TDD in Go with Ginkgo and Gomega

Ginkgo and DependenciesIt(“Saves itself to file”, func() {

configObj.Save(tempDir)

restoredConfig := config.FromFile(configObj.Filename)

Expect(*restoredConfig).To(Equal(*configObj))

})

AfterEach(func() {

os.RemoveAll(tempDir)

})

}) // end Describe()

Page 20: TDD in Go with Ginkgo and Gomega

Ginkgo and Dependencies

● Ginkgo heavily relies on closures and

closure variables to manage dependencies.o Resist the temptation to share state among tests!

● Nested dependencieso Within a Describe block, you can nest:

Context block

More It’s and Before/AfterEach’s

Page 21: TDD in Go with Ginkgo and Gomega

Nested Dependenciesvar _ = Describe(“My component”)

var configObj config.MyConfig

BeforeEach(func() {

configObj = config.NewConfig()

})

It(“Does test1”, func() {

// configObj is setup

})

Page 22: TDD in Go with Ginkgo and Gomega

Nested DependenciesContext(“With a fake HTTP server configured”, func() {

var testServer httptest.Server

BeforeEach(func() {

testServer = httptest.NewServer(<fake handler>)

})

It(“Does test2”, func() {

configObj.ServerUrl = testServer.URL

configObj.DoSomething() ...

})

})

Page 23: TDD in Go with Ginkgo and Gomega

Testing Notation + Ginkgo

● Obviously, Ginkgo was not designed with my

exact notation in mind…

● However, it maps to it without too much

difficulty.

Page 24: TDD in Go with Ginkgo and Gomega

Testing Notation + Ginkgovar _ = Describe(“my component”, func() {

// GIVEN

BeforeEach(func () {

})

It(“Does something useful”, func() {

// WHEN

CallSomething()

// THEN

Expect(stuff).To((Equal(something))

})

})

Page 25: TDD in Go with Ginkgo and Gomega

Alternate Notation Stylevar _ = Describe(“my component”, func() {

BeforeEach(func() {...}) //

GIVEN

It(“Does something useful”, func() { // WHEN

closureVar = CallSomething()

})

AfterEach(func() {

Expect(closureVar).To(Equal(something)) // THEN

})

})

Page 26: TDD in Go with Ginkgo and Gomega

Ginkgo Bootstrap

● Ginkgo integrates with go test

● You must use the ginkgo command to

generate boilerplate code:

$ cd mypackage/ # within $GOPATH/src

$ ginkgo bootstrap

$ ls

mypackage_suite_test.go

Page 27: TDD in Go with Ginkgo and Gomega

Ginkgo Bootstrappackage mypackage_test // NOT THE SAME AS mypackage!!!

import (

. "github.com/onsi/ginkgo"

. "github.com/onsi/gomega"

"testing"

)

func TestConfig(t *testing.T) { // go test integration boilerplate!

RegisterFailHandler(Fail)

RunSpecs(t, "Mypackage Suite")

}

Page 28: TDD in Go with Ginkgo and Gomega

Ginkgo Generate

● The generated suite calls the generated test

files:$ ginkgo generate

$ ls

mypackage_suite_test.go mypackage_test.go

$ cat mypackage_test.go

...

Page 29: TDD in Go with Ginkgo and Gomega

Ginkgo Generatepackage mypackage_test // NOT THE SAME AS mypackage!!

import (

. “mypackage”

. “github.com/onsi/ginkgo”

. “github.com/onsi/gomega”

)

var _ = Describe(“Mypackage test”, func() {

})

Page 30: TDD in Go with Ginkgo and Gomega

Assessing Ginkgo/Gomega

Test Notation

● The BDD-style english annotations help

readability a little bit...

● Closures are a slippery slope!o Too much code: declare closure vars, must also

initialize in BeforeEach to ensure test

idempotency!

Page 31: TDD in Go with Ginkgo and Gomega

Assessing Ginkgo/Gomega

● Complex dependencies are doableo Accomplished by nesting Describe/Context/

BeforeEach’s

o However, your tests must be inserted in ever-more

nested closures

Can get kinda complicated!

● Gomega assertionso Flow nicely when read aloud

o Too complex as a formal notation

Page 32: TDD in Go with Ginkgo and Gomega

Assessing Ginkgo/Gomega

● Overall test notation grade: C+/B-o Depends on my mood and how complicated my test

needs to get.

o Worst part about it is that your test file easily grows

way too long, and thus harder to comprehend as a

spec.

● Good enough to get our job done with high

quality at Tasqr.

Page 33: TDD in Go with Ginkgo and Gomega

Go TDD Roundup

Questions?