gogiven

package module
Version: v1.4.0 Latest Latest
Warning

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

Go to latest
Published: Feb 1, 2018 License: BSD-3-Clause Imports: 8 Imported by: 0

README

gogiven

An alternative BDD spec framework for go. Builds on "go test" tool and builds on the go testing package.

Build status Go Report Card GoDoc Coverage Status

Inspired by YATSPEC. Another similar idea is JGiven, although both are Java based.

Check out the HTML output generator used as a default here: htmlspec - using go's own template/html package.

Feel free to contact me and help improve the code base or let me know if you have any issues or questions!

To contribute, please read this first.

Table of Contents

  1. Introduction
  2. Example One - GoGivens in practice
  3. Example Two - Table Tests
  4. Content Generation
  5. List of pre-written output generators

Introduction

Go Givens is a lightweight TDD framework for producing test specifications directly from the code you write.

Go Givens parses your test file and produces a human-readable output in a specified directory, containing all the tests, captured data and other related information regarding your test such as success or failure.

Go Givens was inspired by YATSPEC, a BDD framework employed extensively by Sky Network Services (part of Sky, a UK tv company). As mentioned above, another similar product is JGiven.

Why?

Capturing your test method as test output is the only real way to show it's intention. You can refactor a test, and have the output update accordingly when the test runs. Unlike other go BDD frameworks, you can use function names to declare intent, and refactoring the function will affect the test. E.g.


//Hello, spec world!
func TestMyTest(...){
	Given(testing, someData()). // This is a test
	When(fooBarBaz()).
	Then(baz())
}

.. will be rendered as (but in html, or json, or whatever):

My Test
Hello, spec world!

Specification

Given testing some data noting that this is a test
When foo bar baz
Then baz 

Test data (set by the func someData in the example above) can be captured in a map of interesting givens, and as the test progresses, the actuals can be captured in a map of captured inputs and outputs.

Interesting whats?

Interesting givens are data points that you want to use in your tests that are important for it to function correctly. "Given" data is then used by your application to fulfil some sort of request or process.

Interesting givens are generated as output along side your test output in a table so interested parties can examine them.

Captured Whos??

Captured inputs and outputs are data points that are registered by either your system under test (stubs, mocks etc) or the output from your system its self.

Captured inputs and outputs are logged along side your test, for each test, so that interested parties can view them.

That's all great, but still.. WHY would I want to log this stuff??

In BDD, a system's inputs and outputs are important to the business. Capturing the relationships between your data and the way the system handles can be demonstrated to your client. For example, your system could call a 3rd party, and you might want to model the interaction with stubs.

GoGivens gives you a standardised way of rendering captured data alongside your tests so you don't have to worry about it.

Rendered how?

The test framework parses your test file, and grabs the content. It strips all non-interesting parts out and leaves the Given/When/Then format in plain text ready for a GoGivensOutputGenerator to process the text. Interesting givens and Captured inputs and outputs are maps, which are rendered alongside your test givens as table data -- interesting givens are tablulated, and captured IO is listed.

A complete example of how to write a GoGivensOutputGenerator is given in the sister project html spec - written in Go.

Example One - GoGivens in Practice

import (
	"github.com/corbym/gocrest/has"
	"github.com/corbym/gocrest/then"
	"github.com/corbym/gogiven/base"
	"github.com/corbym/gogiven/testdata"
	"testing"
	"github.com/corbym/gocrest/is"
)

func TestMain(testmain *testing.M) {
	runOutput := testmain.Run()
	GenerateTestOutput() // You only need test main GenerateTestOutput() if you want to produce HTML output.
	os.Exit(runOutput)
}

func TestMyFirst(testing *testing.T) {
	Given(testing, someDataSetup).

		When(somethingHappens).

		Then(func(testing base.TestingT, actual testdata.CapturedIO, givens testdata.InterestingGivens) { // passed in testing should be used for assertions
		//do assertions
		then.AssertThat(testing, actual["actual"], is.EqualTo("some output"))
	})
}

Note you do not have to use "gocrest" assertions, you can still call all of testing.T's functions to fail the test or you can use any go testing assertion package compatible with testing.T.

When run, the above will produce an HTML output:

Example Html

Example Two - Table Tests

Table tests work the same way as normal go table tests. GoGivens will then mark which test failed, if they do, in your test output.

Example:

...
func TestMyFirst(testing *testing.T){
   var someRange = []struct {
		actual   string
		expected int
	}{
		{actual: "", expected: 0},
		{actual: "a", expected: 2},
	}
	for _, test := range someRange {
	   tst.Run(test.actual, func(weAreTesting *testing.T) {
	   	Given(weAreTesting, someDataSetup).
			When(someAction).
			Then(func(t TestingT, actual CapturedIO, givens InterestingGivens) {
			//do assertions
		AssertThat(t, actual.CapturedIO["actual"], is.EqualTo("some output"))
	   	})
	   }	
	}
}
...

The above test will still fail the test function as far as Go is concerned, but the test output will note that the iteration failed like this:

Ranged Example Html

Note that comments are now rendered. Test function comments appear as part of the spec, and inline comments appear as "Noting that ..". In the above, the comment //do assertions would become "Noting that do assertions".

More Examples

Content Generation

Gogivens comes defaultly configured with an html generator (htmlspec.NewTestOutputGenerator) that is consumed by a file generator (generator.FileOutputGenerator) (see the godoc for more information). The content generator implements the following interface:

type GoGivensOutputGenerator interface {
	Generate(data *PageData) (output io.Reader)
	//ContentType is text/html, application/json or other mime type
	ContentType() string
}

The generated content (output io.Reader) is then consumed by an OutputListener:

type OutputListener interface {
	Notify(testFilePath string, contentType string, output io.Reader)
}

If you want your own output listener just create your own and replace and/or append to the default listeners in your TestMain:

func TestMain(testmain *testing.M) {
	gogiven.OutputListeners = []generator.OutputListener{new(MyFooListener)}
	// or alternately (or inclusively!)
	gogiven.OutputListeners = append(OutputListeners, new(MyBarListener))
	runOutput := testmain.Run()
	gogiven.GenerateTestOutput() // generates the output after the test is finished.
	os.Exit(runOutput)
}

Setting the test file output (for the generator.FileOutputGenerator)

You can add the environment variable GOGIVENS_OUTPUT_DIR to your env properties that points to a directory you want goGivens to report the test output to.

Default is the os's tmp directory.

List of Pre-written Ouput Generators

GoGiven comes with the following output generators:

Documentation

Index

Constants

This section is empty.

Variables

Generator is a global variable that holds the GoGivensOutputGenerator. You can replace the generator with your own if you match the interface here and set Generator = new(myFooGenerator) in a method (usually TestMain or init). Don't forget to add the call to the generator function in a "func TestMain(testing.M)" method in your test package. One file per test file will be generated containing output.

OutputListeners holds a list of listeners which can process the output generated by the GoGivensOutputGenerator. By default, the generator.FileOutputGenerator is added. but you can append or replace with your own.

Functions

func GenerateTestOutput

func GenerateTestOutput()

GenerateTestOutput generates the test output. Call this method from TestMain. The global var Generator is used to generate the content, and the OutputListeners are iterated and Notified of the content that has been generated.

func Given

func Given(testing base.TestingT, given ...base.GivenData) *base.Some

Given sets up some interesting givens for the test. Pass in testing.T here and a function which adds some givens to the map.

*Warning:* if you call this method twice in a test, you will start a new test which will appear in the test output.

func When added in v1.0.1

func When(testing base.TestingT, action ...base.CapturedIOGivenData) *base.Some

When is a shortcut method when no Given is required.

Types

type SafeMap added in v1.4.0

type SafeMap struct {
	sync.RWMutex
	// contains filtered or unexported fields
}

SafeMap is used internally to hold a threadsafe copy of the global test state.

func (*SafeMap) Keys added in v1.4.0

func (rm *SafeMap) Keys() []string

Keys returns an array of keys that the map contains

func (*SafeMap) Load added in v1.4.0

func (rm *SafeMap) Load(key string) (value interface{}, ok bool)

Load a key from the map

func (*SafeMap) Store added in v1.4.0

func (rm *SafeMap) Store(key string, value interface{})

Store a value against a key from the map

type TestContext

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

TestContext contains a SafeMap of the TestMetaData for the current test file being processed.//////

func NewTestContext

func NewTestContext(fileName string) *TestContext

NewTestContext creates a new context.

func (*TestContext) FileName

func (c *TestContext) FileName() string

FileName is a string containing the filename of the test that are being executed.

func (*TestContext) SomeTests

func (c *TestContext) SomeTests() *SafeMap

SomeTests is a map containing the TestMetaData for this TestContext's tests that are being executed.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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