Skip to content

Commit

Permalink
Add a suite package for compat with testify
Browse files Browse the repository at this point in the history
  • Loading branch information
dnephin committed Jan 22, 2018
1 parent 3528a68 commit 79f0b3f
Show file tree
Hide file tree
Showing 3 changed files with 323 additions and 0 deletions.
22 changes: 22 additions & 0 deletions suite/LICENSE
@@ -0,0 +1,22 @@
Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell

Please consider promoting this project if you find it useful.

Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
205 changes: 205 additions & 0 deletions suite/suite.go
@@ -0,0 +1,205 @@
/*Package suite provides compatibility with testify/suite.
Suites can be used to group tests together, and to perform common setup and
teardown for each test in the suite.
TODO: example
Regular expression to select test suites specified command-line
argument "-run". Regular expression to select the methods
of test suites specified command-line argument "-m".
Suite object has assertion methods.
*/
package suite

import (
"flag"
"reflect"
"regexp"
"strings"
"testing"

"github.com/gotestyourself/gotestyourself/assert"
)

var methodPattern = &regexpValue{}

func init() {
flag.Var(methodPattern, "test.m", "only run tests that match the regexp")
}

// TestingSuite used is the interface for a test suite
type TestingSuite interface {
T() *testing.T
SetT(*testing.T)
}

type setupSuite interface {
SetupSuite()
}

type setupTest interface {
SetupTest()
}

type teardownSuite interface {
TearDownSuite()
}

type teardownTest interface {
TearDownTest()
}

type beforeTest interface {
BeforeTest(suiteName, testName string)
}

type afterTest interface {
AfterTest(suiteName, testName string)
}

type helperT interface {
Helper()
}

// Suite is an implementation of TestingSuite which can be embedded in a test
// suite.
type Suite struct {
t *testing.T
}

// T retrieves the current *testing.T context.
func (suite *Suite) T() *testing.T {
return suite.t
}

// SetT sets the current *testing.T context.
func (suite *Suite) SetT(t *testing.T) {
suite.t = t
}

// Assert performs a comparison, marks the test as having failed if the comparison
// returns false, and stops execution immediately.
//
// This is equivalent to assert.Assert(t, comparison).
func (suite *Suite) Assert(comparison assert.BoolOrComparison, msgAndArgs ...interface{}) {
if ht, ok := testing.TB(suite.t).(helperT); ok {
ht.Helper()
}
// TODO: will print `comparison` instead of caller ast when used with bool
assert.Assert(suite.t, comparison, msgAndArgs...)
}

// Check performs a comparison and marks the test as having failed if the comparison
// returns false. Returns the result of the comparison.
func (suite *Suite) Check(comparison assert.BoolOrComparison, msgAndArgs ...interface{}) bool {
if ht, ok := testing.TB(suite.t).(helperT); ok {
ht.Helper()
}
// TODO: will print `comparison` instead of caller ast when used with bool
return assert.Check(suite.t, comparison, msgAndArgs...)
}

// Run all the tests in a testing suite
func Run(t *testing.T, suite TestingSuite) {
suite.SetT(t)

if s, ok := suite.(setupSuite); ok {
s.SetupSuite()
}
defer func() {
if s, ok := suite.(teardownSuite); ok {
s.TearDownSuite()
}
}()

suiteType := reflect.TypeOf(suite)
tests := []testing.InternalTest{}
for index := 0; index < suiteType.NumMethod(); index++ {
method := suiteType.Method(index)
if !isTestMethod(method.Name) {
continue
}
test := testing.InternalTest{
Name: method.Name,
F: newTestFunc(suite, method),
}
tests = append(tests, test)
}
runTests(t, tests)
}

func newTestFunc(suite TestingSuite, method reflect.Method) func(*testing.T) {
suiteType := reflect.TypeOf(suite)
return func(t *testing.T) {
parentT := suite.T()
suite.SetT(t)
if s, ok := suite.(setupTest); ok {
s.SetupTest()
}
if s, ok := suite.(beforeTest); ok {
s.BeforeTest(suiteType.Elem().Name(), method.Name)
}
defer func() {
if s, ok := suite.(afterTest); ok {
s.AfterTest(suiteType.Elem().Name(), method.Name)
}
if s, ok := suite.(teardownTest); ok {
s.TearDownTest()
}
suite.SetT(parentT)
}()
method.Func.Call([]reflect.Value{reflect.ValueOf(suite)})
}
}

type runner interface {
Run(name string, f func(t *testing.T)) bool
}

func runTests(t testing.TB, tests []testing.InternalTest) {
r, ok := t.(runner)
if !ok { // backwards compatibility with Go 1.6 and below
allTestsFilter := func(_, _ string) (bool, error) { return true, nil }
if !testing.RunTests(allTestsFilter, tests) {
t.Fail()
}
return
}

for _, test := range tests {
r.Run(test.Name, test.F)
}
}

// TODO: should also check the next character after Test is uppercase
func isTestMethod(name string) bool {
if !strings.HasPrefix(name, "Test") {
return false
}
return methodPattern.Match(name)
}

type regexpValue struct {
re *regexp.Regexp
}

func (v *regexpValue) String() string {
if v.re == nil {
return ""
}
return v.re.String()
}

func (v *regexpValue) Set(value string) error {
re, err := regexp.Compile(value)
v.re = re
return err
}

func (v *regexpValue) Match(value string) bool {
if v.re == nil {
return true
}
return v.re.MatchString(value)
}
96 changes: 96 additions & 0 deletions suite/suite_test.go
@@ -0,0 +1,96 @@
package suite

import (
"testing"

"github.com/gotestyourself/gotestyourself/assert"
is "github.com/gotestyourself/gotestyourself/assert/cmp"
)

type fakeSuite struct {
Suite

suiteT *testing.T
counter int

beforeTestCalls []string
testCalls []string
afterTestCalls []string
}

func (s *fakeSuite) assertAndIncrement(expected int) {
if ht, ok := testing.TB(s.t).(helperT); ok {
ht.Helper()
}
s.Assert(is.Equal(s.counter, expected))
s.counter++
}

func (s *fakeSuite) baseCount() int {
return len(s.afterTestCalls) * 4
}

func (s *fakeSuite) SetupSuite() {
s.assertAndIncrement(0)
s.Assert(is.Equal(s.suiteT, s.T()))
}

func (s *fakeSuite) SetupTest() {
s.assertAndIncrement(s.baseCount() + 1)
s.Assert(s.suiteT != s.T())
}

func (s *fakeSuite) BeforeTest(suiteName, testName string) {
s.assertAndIncrement(s.baseCount() + 2)
s.Assert(is.Equal(suiteName, "fakeSuite"))
s.beforeTestCalls = append(s.beforeTestCalls, testName)
s.Assert(s.suiteT != s.T())
}

func (s *fakeSuite) AfterTest(suiteName, testName string) {
s.assertAndIncrement(s.baseCount() + 3)
s.Assert(is.Equal(suiteName, "fakeSuite"))
s.afterTestCalls = append(s.afterTestCalls, testName)
s.Assert(s.suiteT != s.T())
}

func (s *fakeSuite) TearDownTest() {
s.assertAndIncrement(s.baseCount())
s.Assert(s.suiteT != s.T())
}

func (s *fakeSuite) TearDownSuite() {
s.assertAndIncrement(s.baseCount() + 1)
s.Assert(is.Equal(s.suiteT, s.T()))
}

func (s *fakeSuite) TestOne() {
s.testCalls = append(s.testCalls, "TestOne")
s.Assert(s.suiteT != s.T())
}

func (s *fakeSuite) TestTwo() {
s.testCalls = append(s.testCalls, "TestTwo")
}

func (s *fakeSuite) TestSkip() {
s.testCalls = append(s.testCalls, "TestSkip")
s.T().Skip()
}

func (s *fakeSuite) NonATestMethod() {
}

func TestRunSuite(t *testing.T) {
fakeSuite := new(fakeSuite)
fakeSuite.suiteT = t
Run(t, fakeSuite)

expectedCount := 14 // setupSuite=1 + teardownSuite=1 + (numTests=3 * numFixtures=4)
assert.Equal(t, fakeSuite.counter, expectedCount)

expected := []string{"TestOne", "TestSkip", "TestTwo"}
assert.Assert(t, is.Compare(expected, fakeSuite.testCalls))
assert.Assert(t, is.Compare(expected, fakeSuite.afterTestCalls))
assert.Assert(t, is.Compare(expected, fakeSuite.beforeTestCalls))
}

0 comments on commit 79f0b3f

Please sign in to comment.