Giter VIP home page Giter VIP logo

gunit's Introduction

SMARTY DISCLAIMER: Subject to the terms of the associated license agreement, this software is freely available for your use. This software is FREE, AS IN PUPPIES, and is a gift. Enjoy your new responsibility. This means that while we may consider enhancement requests, we may or may not choose to entertain requests at our sole and absolute discretion.

Build Status Code Coverage Go Report Card GoDoc

gunit

Installation

$ go get github.com/smarty/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 (
    "time"
	"testing"

	"github.com/smarty/assertions/should"
	"github.com/smarty/gunit"
)

func TestExampleFixture(t *testing.T) {
	gunit.Run(new(ExampleFixture), t)
}

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 (this *ExampleFixture) SetupStuff() {
	// This optional method will be executed before each "Test"
	// method (because it starts with "Setup").
}
func (this *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 (this *ExampleFixture) TestWithAssertions() {
	// Here's how to use the functions from the `should`
	// package at github.com/smarty/assertions/should
	// to perform assertions:
	this.So(42, should.Equal, 42)
	this.So("Hello, World!", should.ContainSubstring, "World")
}

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

func (this *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)
	this.So(true, should.BeTrue)
}

So, I see just one traditional test function and it's only one line long. What's the deal with that?

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. All you have to do is create a Test function and pass a new instance of your fixture struct to gunit's Run function along with the *testing.T and it will run all defined Test methods along with the Setup and Teardown method.

Enjoy.

Parallelism

By default all fixtures are run in parallel as they should be independent, but if you for some reason have fixtures which need to be run sequentially, you can change the Run() method to RunSequential(), e.g. in the above example

func TestExampleFixture(t *testing.T) {
	gunit.RunSequential(new(ExampleFixture), t)
}

Advanced Examples


For users of JetBrains IDEs, here's LiveTemplate you can use for generating the scaffolding for a new fixture:

  • Abbreviation: fixture
  • Description: Generate gunit Fixture boilerplate
  • Template Text:
func Test$NAME$Fixture(t *testing.T) {
    gunit.Run(new($NAME$Fixture), t)
}

type $NAME$Fixture struct {
    *gunit.Fixture
}

func (this *$NAME$Fixture) Setup() {
}

func (this *$NAME$Fixture) Test$END$() {
}

Be sure to specify that this LiveTemplate is applicable in Go files.

gunit's People

Contributors

dwhatcott avatar eclipseo avatar jcrob2 avatar mdwhatcott avatar mouaying avatar nathantchan avatar pmenglund avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

gunit's Issues

Inadvertently leaving 'Focus' mode engaged leads to false positive test results

We've all been there. A few tests aren't passing. We want to debug one of them. We add the Focus prefix to the test name and set a few breakpoints. After emerging from the rabbit hole bearing the gleaming fur of several newly shorn yaks we go on our merry way, blissfully forgetful that the 'Focus' prefix still remains. Sooner or later when we expect a test to fail and it doesn't we sense a disturbance in the force...

fixture_test.go currently fails

After a fresh repo clone, running go 1.9.2, I cannot get the tests to pass:

go test
--- FAIL: TestPanicIsRecoveredAndPrintedByFinalize (0.00s)
	fixture_test.go:300: Expected string containing stack trace information...
FAIL
exit status 1

Output of failed assertion in Setup/TearDown method lacks test name and originating file:line info

func (this *MyFixture) TestSomething() {
    // ...
}

func (this *MyFixture) Teardown() {
    this.So(true, should.BeFalse)
}

Test Output:

    	fixture.go:96: 
    		Test:     .()
    		(0):      /Users/mike/src/github.com/smartystreets/stuff/stuff.go:6
    		Expected: false
    		Actual:   true

Notice, there's no Test name (instead: .()) and there's only the file:line info for the exact line of the assertion, any indicator of the file:line of the current test is missing. Would it be possible to back-fill that information from within the deferred Teardown call?

Expected Test Output:

    	fixture.go:96: 
    		Test:     MyFixture.TestSomething()
    		(1):      /Users/mike/src/github.com/smartystreets/stuff/stuff.go:6
                (0):      /Users/mike/src/github.com/smartystreets/stuff/stuff.go:1
    		Expected: false
    		Actual:   true

Support multiple Setup and Teardown functions on a fixture

It would be nice to be able to run multiple independent fixture.Setup* and fixture.Teardown* functions. My use case is a functional test of several interacting components together.

Here is my current workaround:

func (this *FunctionalFixture) Setup() {
	var wg sync.WaitGroup
	wg.Add(2)
	go func() {
		this.setup1()
		wg.Done()
	}()
	go func() {
		this.setup2()
		wg.Done()
	}()
        wg.Wait()
}

Output the word `Test` in console?

How should this test log when output to the console:

TestA_Equals_B

Should it be A equals B or Test A equals B

For that matter do we want to change equals to =?

Output of skipped tests lacks correct originating file:line info

This is currently what you get when you mark a test as skipped:

--- SKIP: TestBlahFixture/SkipTestBlah (0.00s)
test_case.go:48: Skipped test

The test_case.go:48 info is from the bowels of the gunit code, not the actual test that was skipped. I've tried playing around with *testing.T.Helper() to try and get around this, but to no avail. (I think calling t.Skip() cancels the current goroutine, causing you to lose track of the stack.) This might be another reason to scan the AST and gather up file:line info for all test methods.

Panic on assertion failure

Go version
go version go1.11.6 linux/amd64

What I did
Created a test which fails

What I expected
A relevant error message and a more concise stack trace.

What happened
panic. index out of range is not relevant to the assertion.

Example code

package function

import (
	"testing"

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

func TestExampleFixture(t *testing.T) {
	gunit.Run(new(ExampleFixture), t)
}

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

func (this *ExampleFixture) TestWithAssertions() {
	this.So(1, should.Equal, 2)
}

Modules

module gunit_runtime_error

require (
	github.com/smartystreets/assertions v1.0.1
	github.com/smartystreets/gunit v1.3.1
)

Output

--- FAIL: TestExampleFixture (0.00s)
    --- FAIL: TestExampleFixture/TestWithAssertions (0.00s)
        test_case.go:67: Test definition:
            /gunit_runtime_error/gunit_test.go:18
        fixture.go:102: 
            PANIC: runtime error: index out of range
            ...
            github.com/smartystreets/gunit/reports.(*failureReport).extractLineOfCode(0xc000083a00, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1)
            	/gopath/pkg/mod/github.com/smartystreets/[email protected]/reports/failure_report.go:63 +0xc1
            github.com/smartystreets/gunit/reports.(*failureReport).scanStack(0xc000083a00, 0xc0000f2000, 0x1f, 0x20)
            	/gopath/pkg/mod/github.com/smartystreets/[email protected]/reports/failure_report.go:45 +0x1e9
            github.com/smartystreets/gunit/reports.FailureReport(0xc0000942d0, 0x2d, 0xc0000f2000, 0x1f, 0x20, 0x57e880, 0x5e9f10)
            	/gopath/pkg/mod/github.com/smartystreets/[email protected]/reports/failure_report.go:15 +0xf7
            github.com/smartystreets/gunit.(*Fixture).fail(0xc0000a0a80, 0xc0000942d0, 0x2d)
            	/gopath/pkg/mod/github.com/smartystreets/[email protected]/fixture.go:93 +0x71
            github.com/smartystreets/gunit.(*Fixture).So(0xc0000a0a80, 0x57e880, 0x5e9f10, 0x5cd0b0, 0xc00008aa50, 0x1, 0x1, 0x57f300)
            	/gopath/pkg/mod/github.com/smartystreets/[email protected]/fixture.go:49 +0x9d
            gunit_runtime_error.(*ExampleFixture).TestWithAssertions(0xc00009c268)
            	/gunit_runtime_error/gunit_test.go:19 +0x99
            
FAIL
FAIL	gunit_runtime_error	0.002s

Sentence separators

Input: TestWhenLastLineComponentsAreProvided_ThenAddressFieldsSupplyDeliveryLines
Current Output: When last line components are provided then address fields supply delivery lines
Proposed Output: When last line components are provided, then address fields supply delivery lines

(The underscore in the name causes a comma to be inserted.)

Not compatible with Testify

I'm not sure if this is a problem with Testify or this framework, so I'm starting here.

Expected Behavior

Test passes

Actual Behavior

Runtime Panic

Minimum Repro steps

example_test.go

package foo

import (
	"testing"
	"github.com/smartystreets/gunit"
	"github.com/stretchr/testify/assert"
)

func Test(t *testing.T) {
		f := new(ExampleFixture)
		f.t = t
    gunit.Run(f, t)
}

type ExampleFixture struct {
    *gunit.Fixture
    t *testing.T
}

func (this *ExampleFixture) TestFoo() {
	assert.Equal(this.t, 123, 123, "they should be equal")
}

Output:

$ go test ./maps/foo/example_test.go
--- FAIL: Test (0.00s)
    --- FAIL: Test/TestFoo (0.00s)
    	test_case.go:67: Test definition:

    	fixture.go:97:
    		PANIC: runtime error: invalid memory address or nil pointer dereference
    		goroutine 21 [running]:
    		github.com/smartystreets/gunit.(*Fixture).recoverPanic(0xc420134e80, 0x131ac80, 0x154b470)
    			/Users/wes/go/src/github.com/smartystreets/gunit/fixture.go:103 +0xf6
    		github.com/smartystreets/gunit.(*Fixture).finalize(0xc420134e80)
    			/Users/wes/go/src/github.com/smartystreets/gunit/fixture.go:93 +0x22f
    		panic(0x131ac80, 0x154b470)
    			/usr/local/Cellar/go/1.10/libexec/src/runtime/panic.go:505 +0x229
    		testing.(*T).Helper(0x0)
    			<autogenerated>:1 +0x5
    		github.com/stretchr/testify/assert.Equal(0x13b6bc0, 0x0, 0x12fb5e0, 0x13b4268, 0x12fb5e0, 0x13b4270, 0xc420060b68, 0x1, 0x1, 0xc420060ba8)
    			/Users/wes/go/src/github.com/stretchr/testify/assert/assertions.go:328 +0x480
    		command-line-arguments.(*ExampleFixture).TestFoo(0xc4200b3260)
    			/Users/wes/projects/space-z/maps/foo/example_test.go:21 +0xa4
    		reflect.Value.call(0x1364e00, 0xc4200b3260, 0x3613, 0x1379ce4, 0x4, 0x0, 0x0, 0x0, 0xf, 0x114d20e, ...)
    			/usr/local/Cellar/go/1.10/libexec/src/reflect/value.go:447 +0x969
    		reflect.Value.Call(0x1364e00, 0xc4200b3260, 0x3613, 0x0, 0x0, 0x0, 0x3613, 0xc420134e80, 0xc420060f40)
    			/usr/local/Cellar/go/1.10/libexec/src/reflect/value.go:308 +0xa4
    		github.com/smartystreets/gunit.(*testCase).runTest(0xc42014a9a0)
    			/Users/wes/go/src/github.com/smartystreets/gunit/test_case.go:86 +0x7c
    		github.com/smartystreets/gunit.(*testCase).runWithSetupAndTeardown(0xc42014a9a0)
    			/Users/wes/go/src/github.com/smartystreets/gunit/test_case.go:76 +0x5f
    		github.com/smartystreets/gunit.(*testCase).run(0xc42014a9a0, 0xc4201561e0)
    			/Users/wes/go/src/github.com/smartystreets/gunit/test_case.go:64 +0x79
    		github.com/smartystreets/gunit.(*testCase).(github.com/smartystreets/gunit.run)-fm(0xc4201561e0)
    			/Users/wes/go/src/github.com/smartystreets/gunit/test_case.go:48 +0x34
    		testing.tRunner(0xc4201561e0, 0xc4200b3210)
    			/usr/local/Cellar/go/1.10/libexec/src/testing/testing.go:777 +0xd0
    		created by testing.(*T).Run
    			/usr/local/Cellar/go/1.10/libexec/src/testing/testing.go:824 +0x2e0


FAIL
FAIL	command-line-arguments	0.010s

Define 'FocusTest' debugging prefix to prevent all other test cases from running

Currently, to run a single test you have to skip all other tests:

func (this *MyFixture) TestA() {}
func (this *MyFixture) SkipTestB() {}
func (this *MyFixture) SkipTestC() {}

What I'd like to enable is a simple marker on the desired test:

func (this *MyFixture) FocusTestA() {}
func (this *MyFixture) TestB() {}
func (this *MyFixture) TestC() {}

Of course, this would be a non-issue if we had more full-fledged support from Goland, but this would also allow users of other IDEs to accomplish this behavior.

Can't share fixtures through struct embedding

I have a couple of tests that require temporary directories. I tried to set up a shared fixture along these lines:

// Embeddable sub-fixture for temporary directories
type tempdirFixture struct {
	*gunit.Fixture
	tempDir string
}

func (f *tempdirFixture) SetupTempDir() {
	tempDir, err := ioutil.TempDir("", "tempdirFixture")
	f.So(err, should.BeNil) // or assertions.ShouldBeNil(err)
	f.tempDir = tempDir
}

func (f *tempdirFixture) TeardownTempDir() {
	err := os.RemoveAll(f.tempDir)
	f.So(err, should.BeNil) // or assertions.ShouldBeNil(err)
}

// plus a few other utility methods for things like creating dummy files

However, when I try embedding this in my test fixture proper, e.g.

type MyFixture {
	tempdirFixture
}

I inevitably get either panic: reflect: indirection through nil pointer to embedded struct or PANIC: runtime error: invalid memory address or nil pointer dereference, sometimes in a test method, sometimes in tempdirFixture.SetupTempDir(), sometimes deep in reflection code where I can't follow what it's doing, depending on exactly how I set up the embeds and initialize the fixtures. (I've tried embedding *tempdirFixture instead of tempdirFixture; I've tried moving *gunit.Fixture from tempdirFixture to the test fixture proper; I've tried various combinations of new() and &Type{}.)

Is this a use case you've ever tried to cover with gunit? And if so, what's the right way to do it?

Obviously I can give up and manually initialize things, but I'd like to take advantage of gunit's reflection-based setup/teardown autodetection.

Full-fledged Goland support

Currently, we can run a test function (which runs an entire Fixture) and we can view all the test results, but there isn't a clean way to run a single test from the green arrow in the gutter or navigate from the test name in the test results gutter back to the test method. It really would be nice to have more full-fledged support for gunit as a test framework/library from the IDE.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.