gunit

package module
v0.0.0-...-0dc4fe5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 14, 2016 License: MIT Imports: 10 Imported by: 0

README

gunit

Installation

$ go get github.com/smartystreets/gunit/gunit

We now present gunit, yet another testing tool for Go.

Not again... (GoConvey was crazy enough...but sort of cool, ok I'll pay attention...)

No wait, this tool has some very interesting properties. It's a mix of good things provided by the built-in testing package, the assertions you know and love from the GoConvey project, the xUnit testing style (the first real unit testing framework), and it's all glued together with go test.

Blah, blah, yeah, yeah. Ok, so what's wrong with just using the standard "testing" package? What's better about this gunit thing?

The convention established by the "testing" package and the go test tool only allows for local function scope:

func TestSomething(t *testing.T) {
	// blah blah blah
}

This limited scope makes extracting functions or structs inconvenient as state will have to be passed to such extractions or state returned from them. It can get messy to keep a test nice and short. Here's the basic idea of what the test author using gunit would implement in a *_test.go file:


package examples

import (
	"github.com/smartystreets/assertions/should"
	"github.com/smartystreets/gunit"
)

type ExampleFixture struct {
	*gunit.Fixture // Required: Embedding this type is what makes the magic happen.

	// Declare useful state here (probably the stuff being tested, any fakes, etc...).
}

func (self *ExampleFixture) SetupStuff() {
	// This optional method will be executed before each "Test"
	// method (because it starts with "Setup").
}
func (self *ExampleFixture) TeardownStuff() {
	// This optional method will be executed after each "Test"
	// method (because it starts with "Teardown"), even if the test method panics.
}


// This is an actual test case:
func (self *ExampleFixture) TestWithAssertions() {
	// Here's how to use the functions from the `should`
	// package at github.com/smartystreets/assertions/should
	// to perform assertions:
	self.So(42, should.Equal, 42)
	self.So("Hello, World!", should.ContainSubstring, "World")
}

func (self *ExampleFixture) SkipTestWithNothing() {
	// Because this method's name starts with 'Skip', it will be skipped.
}

func (self *ExampleFixture) LongTestSlowOperation() {
	// Because this method's name starts with 'Long', it will be skipped if `go test` is run with the `short` flag.
	time.Sleep(time.Hour)
	So(true, should.BeTrue)
}

So, this doesn't import the standard go testing package at all, right? Do I have to run some other command to run my tests?

You're correct, the code you see above doesn't have anything to do with the "testing" package. But you still run go test to execute those tests...

Wait, without any test functions (you know, func TestSomething(t *testing.T) {...}) and without any reference to *testing.T anywhere, how do you mark a test as failed? You're saying I can still run go test? I don't get it.

Astute observations. gunit allows the test author to use a struct as the scope for a group of related test cases, in the style of xUnit fixtures. This makes extraction of setup/teardown behavior (as well as invoking the system under test) much simpler because all state for the test can be declared as fields on a struct which embeds the Fixture type from the gunit package.

Your question about the missing func Test... and the non-existent *testing.T is relevant. The missing link is a command that comes with the gunit project that scans your test fixtures and generates test functions that call all the appropriate methods for you! *testing.T is wrapped up by that generated code and you don't need to worry about calling any methods on it.

Wow, that sounds strangely cool and border-line wrong at the same time.

In either case, here's what the generated code looks like:


//////////////////////////////////////////////////////////////////////////////
// Generated Code ////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////

package examples

import (
	"testing"

	"github.com/smartystreets/gunit"
)

///////////////////////////////////////////////////////////////////////////////

func Test_ExampleFixture__with_assertions(t *testing.T) {
	t.Parallel()
	fixture := gunit.NewFixture(t, testing.Verbose())
	defer fixture.Finalize()
	test := &ExampleFixture{Fixture: fixture}
	defer test.TeardownStuff()
	test.SetupStuff()
	test.TestWithAssertions()
}

func Test_ExampleFixture__skip_with_nothing(t *testing.T) {
	t.Skip("Skipping test case: 'SkipTestWithNothing'")

	t.Parallel()
	fixture := gunit.NewFixture(t, testing.Verbose())
	defer fixture.Finalize()
	test := &ExampleFixture{Fixture: fixture}
	defer test.TeardownStuff()
	test.SetupStuff()
	test.SkipTestWithNothing()
}

///////////////////////////////////////////////////////////////////////////////

func init() {
	gunit.Validate("98884d1f827ddcee8a923e672c3cf2ba")
}

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////// Generated Code //
///////////////////////////////////////////////////////////////////////////////

What's with the init function at the bottom of the generated code?

Ah, that. The call to gunit.Validate passes an md5 checksum of the contents of all *.go files in the package when the code was generated. Any change to those files between initial generation and test execution will necessitate regenerating the code, by running the gunit command.

Oh, so that prevents your struct-based tests from getting out of sync with the test functions go test expects to run.

Exactly. And you can invoke the gunit command be calling go generate if you put the following comment somewhere in your package (even in a *_test.go file):

//go:generate gunit

We use a script that runs our tests automatically whenever a *.go file changes (and it also runs go generate). Depending on the number of test fixtures in a package it generally takes a hundredth of a second to run. Your mileage may vary. Enjoy.

Advanced Examples


Documentation

Overview

Package gunit provides "testing" package hooks and convenience functions for writing tests in an xUnit style. NOTE: Only some of the exported names in this package are meant to be referenced by users of this package:

- Fixture // (as an embedded field on your xUnit-style struct) - Fixture.So(...) // (as a convenient assertion method: So(expected, should.Equal, actual)) - Fixture.Ok(...) // (as a convenient boolean assertion method: Ok(condition, optionalMessage)) - Fixture.Error(...) // (works just like *testing.T.Error(...)) - Fixture.Errorf(...) // (works just like *testing.T.Errorf(...)) - Fixture.Print(...) // (works just like fmt.Print) - Fixture.Printf(...) // (works just like fmt.Printf) - Fixture.Println(...) // (works just like fmt.Println)

The rest are called from code generated by the command at github.com/smartystreets/gunit/gunit. Please see the README file and the examples folder for examples.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func FormatFailureContext

func FormatFailureContext(lineNumber int, code string) string

func Validate

func Validate(checksum string)

Validate is called by generated code.

Types

type Fixture

type Fixture struct {
	// contains filtered or unexported fields
}

Fixture keeps track of test status (failed, passed, skipped) and handles custom logging for xUnit style tests as an embedded field.

func NewFixture

func NewFixture(t TT, verbose bool, code string) *Fixture

NewFixture is called by generated code.

func (*Fixture) Error

func (self *Fixture) Error(args ...interface{})

func (*Fixture) Errorf

func (self *Fixture) Errorf(format string, args ...interface{})

func (*Fixture) Finalize

func (self *Fixture) Finalize()

Finalize is called by generated code.

func (*Fixture) Ok

func (self *Fixture) Ok(condition bool, messages ...string)

func (*Fixture) Print

func (self *Fixture) Print(a ...interface{}) (n int, err error)

Print is analogous to fmt.Print and is ideal for printing in the middle of a test case.

func (*Fixture) Printf

func (self *Fixture) Printf(format string, a ...interface{}) (n int, err error)

Printf is analogous to fmt.Printf and is ideal for printing in the middle of a test case.

func (*Fixture) Println

func (self *Fixture) Println(a ...interface{}) (n int, err error)

Println is analogous to fmt.Println and is ideal for printing in the middle of a test case.

func (*Fixture) So

func (self *Fixture) So(actual interface{}, assert func(actual interface{}, expected ...interface{}) string, expected ...interface{}) bool

So is a convenience method for reporting assertion failure messages, say from the assertion functions found in github.com/smartystreets/assertions/should. Example: self.So(actual, should.Equal, expected)

type TT

type TT interface {
	Log(args ...interface{})
	Fail()
	Failed() bool
	SkipNow()
}

TT represents the functional subset from *testing.T needed by Fixture.

Directories

Path Synopsis
gunit generates testing functions by scanning for xunit-style struct-based fixtures that implement gunit test fixtures (see github.com/smartystreets/gunit).
gunit generates testing functions by scanning for xunit-style struct-based fixtures that implement gunit test fixtures (see github.com/smartystreets/gunit).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL