Skip to content

Commit

Permalink
Merge d866dea into 60895f2
Browse files Browse the repository at this point in the history
  • Loading branch information
maxatome committed Jun 19, 2018
2 parents 60895f2 + d866dea commit f246493
Show file tree
Hide file tree
Showing 10 changed files with 251 additions and 55 deletions.
39 changes: 26 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,25 @@ go-testdeep
[![Go Report Card](https://goreportcard.com/badge/github.com/maxatome/go-testdeep)](https://goreportcard.com/report/github.com/maxatome/go-testdeep)
[![GoDoc](https://godoc.org/github.com/maxatome/go-testdeep?status.svg)](https://godoc.org/github.com/maxatome/go-testdeep)

Golang package `testdeep` allows extremely flexible deep comparison,
built for testing.
Extremely flexible golang deep comparison, extends the go testing package.

- [Latest new](#latest-news)
- [Synopsis](#synopsis)
- [Installation](#installation)
- [Presentation](#presentation)
- [Available operators](#available-operators)
- [License](#license)


## Latest news

- 2018/06/19: new
[ContextConfig](https://godoc.org/github.com/maxatome/go-testdeep#ContextConfig)
feature `FailureIsFatal` available. See
[DefaultContextConfig](https://godoc.org/github.com/maxatome/go-testdeep#pkg-variables)
for default global value and
[`T.FailureIsFatal`](https://godoc.org/github.com/maxatome/go-testdeep#T.FailureIsFatal)
method;
- 2018/06/17: new
[`CmpPanic`](https://godoc.org/github.com/maxatome/go-testdeep#CmpPanic)
&
Expand All @@ -27,7 +40,6 @@ built for testing.
[`CmpSmuggle`](https://godoc.org/github.com/maxatome/go-testdeep#CmpSmuggle)
&
[`T.Smuggle`](https://godoc.org/github.com/maxatome/go-testdeep#T.Smuggle));
- 2018/06/11: `DefaultContextConfig.MaxErrors` defaults to 10 (was 1);
- see [commits history](https://github.com/maxatome/go-testdeep/commits/master)
for other/older changes.

Expand Down Expand Up @@ -56,15 +68,16 @@ func TestCreateRecord(t *testing.T) {
record, err := CreateRecord("Bob", 23)

if td.CmpNoError(t, err) {
td.CmpStruct(t, record,
&Record{
Name: "Bob",
Age: 23,
},
td.StructFields{
"Id": td.NotZero(),
"CreatedAt": td.Between(before, time.Now()),
},
td.CmpDeeply(t, record,
td.Struct(
&Record{
Name: "Bob",
Age: 23,
},
td.StructFields{
"Id": td.NotZero(),
"CreatedAt": td.Between(before, time.Now()),
}),
"Newly created record")
}
}
Expand Down Expand Up @@ -255,7 +268,7 @@ func TestCreateRecord(t *testing.T) {

if td.CmpDeeply(t, err, nil) {
td.CmpDeeply(t, record,
Struct(
td.Struct(
Record{
Name: "Bob",
Age: 23,
Expand Down
10 changes: 7 additions & 3 deletions cmp_deeply.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
"strings"
)

func formatError(t TestingT, err *Error, args ...interface{}) {
func formatError(t TestingT, isFatal bool, err *Error, args ...interface{}) {
t.Helper()

const failedTest = "Failed test"
Expand All @@ -40,7 +40,11 @@ func formatError(t TestingT, err *Error, args ...interface{}) {

err.Append(buf, "")

t.Error(buf.String())
if isFatal {
t.Fatal(buf.String())
} else {
t.Error(buf.String())
}
}

func cmpDeeply(ctx Context, t TestingT, got, expected interface{},
Expand All @@ -51,7 +55,7 @@ func cmpDeeply(ctx Context, t TestingT, got, expected interface{},
return true
}
t.Helper()
formatError(t, err, args...)
formatError(t, ctx.failureIsFatal, err, args...)
return false
}

Expand Down
61 changes: 28 additions & 33 deletions cmp_deeply_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,9 @@ package testdeep

import (
"bytes"
"fmt"
"testing"
)

type TestTestingT struct {
LastMessage string
}

func (t *TestTestingT) Error(args ...interface{}) {
t.LastMessage = fmt.Sprint(args...)
}

func (t *TestTestingT) Helper() {
// Do nothing
}

func TestFormatError(t *testing.T) {
ttt := &TestTestingT{}

Expand All @@ -35,41 +22,49 @@ func TestFormatError(t *testing.T) {

nonStringName := bytes.NewBufferString("zip!")

//
// Without args
formatError(ttt, err)
equalStr(t, ttt.LastMessage, `Failed test
for _, fatal := range []bool{false, true} {
//
// Without args
formatError(ttt, fatal, err)
equalStr(t, ttt.LastMessage, `Failed test
DATA: test error message
test error summary`)
equalBool(t, ttt.IsFatal, fatal)

//
// With one arg
formatError(ttt, err, "foo bar!")
equalStr(t, ttt.LastMessage, `Failed test 'foo bar!'
//
// With one arg
formatError(ttt, fatal, err, "foo bar!")
equalStr(t, ttt.LastMessage, `Failed test 'foo bar!'
DATA: test error message
test error summary`)
equalBool(t, ttt.IsFatal, fatal)

formatError(ttt, err, nonStringName)
equalStr(t, ttt.LastMessage, `Failed test 'zip!'
formatError(ttt, fatal, err, nonStringName)
equalStr(t, ttt.LastMessage, `Failed test 'zip!'
DATA: test error message
test error summary`)
equalBool(t, ttt.IsFatal, fatal)

//
// With several args & Printf format
formatError(ttt, err, "hello %d!", 123)
equalStr(t, ttt.LastMessage, `Failed test 'hello 123!'
//
// With several args & Printf format
formatError(ttt, fatal, err, "hello %d!", 123)
equalStr(t, ttt.LastMessage, `Failed test 'hello 123!'
DATA: test error message
test error summary`)
equalBool(t, ttt.IsFatal, fatal)

//
// With several args without Printf format
formatError(ttt, err, "hello ", "world! ", 123)
equalStr(t, ttt.LastMessage, `Failed test 'hello world! 123'
//
// With several args without Printf format
formatError(ttt, fatal, err, "hello ", "world! ", 123)
equalStr(t, ttt.LastMessage, `Failed test 'hello world! 123'
DATA: test error message
test error summary`)
equalBool(t, ttt.IsFatal, fatal)

formatError(ttt, err, nonStringName, "hello ", "world! ", 123)
equalStr(t, ttt.LastMessage, `Failed test 'zip!hello world! 123'
formatError(ttt, fatal, err, nonStringName, "hello ", "world! ", 123)
equalStr(t, ttt.LastMessage, `Failed test 'zip!hello world! 123'
DATA: test error message
test error summary`)
equalBool(t, ttt.IsFatal, fatal)
}
}
4 changes: 4 additions & 0 deletions cmp_funcs_misc.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ func cmpError(ctx Context, t TestingT, got error, args ...interface{}) bool {

t.Helper()
formatError(t,
ctx.failureIsFatal,
&Error{
Context: ctx,
Message: "should be an error",
Expand All @@ -63,6 +64,7 @@ func cmpNoError(ctx Context, t TestingT, got error, args ...interface{}) bool {

t.Helper()
formatError(t,
ctx.failureIsFatal,
&Error{
Context: ctx,
Message: "should NOT be an error",
Expand Down Expand Up @@ -125,6 +127,7 @@ func cmpPanic(ctx Context, t TestingT, fn func(), expected interface{}, args ...

if !panicked {
formatError(t,
ctx.failureIsFatal,
&Error{
Context: ctx,
Message: "should have panicked",
Expand Down Expand Up @@ -174,6 +177,7 @@ func cmpNotPanic(ctx Context, t TestingT, fn func(), args ...interface{}) bool {
}

formatError(t,
ctx.failureIsFatal,
&Error{
Context: ctx,
Message: "should NOT have panicked",
Expand Down
19 changes: 14 additions & 5 deletions context.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ type ContextConfig struct {
// Setting it to a negative number means no limit: all errors
// will be dumped.
MaxErrors int
// FailureIsFatal allows to Fatal() (instead of Error()) when a test
// fails. Using *testing.T instance as
// t.TestingFT value, FailNow() is called behind the scenes when
// Fatal() is called. See testing documentation for details.
FailureIsFatal bool
}

const (
Expand All @@ -59,8 +64,9 @@ func getMaxErrorsFromEnv() int {
// tests failures. If overridden, new settings will impact all Cmp*
// functions and *T methods (if not specifically configured.)
var DefaultContextConfig = ContextConfig{
RootName: contextDefaultRootName,
MaxErrors: getMaxErrorsFromEnv(),
RootName: contextDefaultRootName,
MaxErrors: getMaxErrorsFromEnv(),
FailureIsFatal: false,
}

func (c *ContextConfig) sanitize() {
Expand Down Expand Up @@ -96,6 +102,8 @@ type Context struct {
// < 0 do not stop until comparison ends.
maxErrors int
errors *[]*Error
// See ContextConfig.FailureIsFatal for details
failureIsFatal bool
}

// NewContext creates a new Context using DefaultContextConfig configuration.
Expand All @@ -108,9 +116,10 @@ func NewContextWithConfig(config ContextConfig) (ctx Context) {
config.sanitize()

ctx = Context{
path: config.RootName,
visited: map[visit]bool{},
maxErrors: config.MaxErrors,
path: config.RootName,
visited: map[visit]bool{},
maxErrors: config.MaxErrors,
failureIsFatal: config.FailureIsFatal,
}

ctx.initErrors()
Expand Down
12 changes: 12 additions & 0 deletions context_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,18 @@ func equalInt(t *testing.T, got, expected int) bool {
return false
}

func equalBool(t *testing.T, got, expected bool) bool {
if got == expected {
return true
}

t.Helper()
t.Errorf(`Failed test
got: %t
expected: %t`, got, expected)
return false
}

func TestContext(t *testing.T) {
equalStr(t, NewContext().path, "DATA")
equalStr(t, NewBooleanContext().path, "")
Expand Down
31 changes: 31 additions & 0 deletions t_struct.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,37 @@ func (t *T) RootName(rootName string) *T {
return &new
}

// FailureIsFatal allows to choose whether t.TestingFT.Fatal() or
// t.TestingFT.Error() will be used to print the next failure
// reports. When "enable" is true (or missing) testing.Fatal() will be
// called, else testing.Error(). Using *testing.T instance as
// t.TestingFT value, FailNow() is called behind the scenes when
// Fatal() is called. See testing documentation for details.
//
// It returns a new instance of *T so does not alter the original t
// and used as follows:
//
// // Following t.CmpDeeply() will call Fatal() if failure
// t = t.FailureIsFatal()
// t.CmpDeeply(...)
// t.CmpDeeply(...)
// // Following t.CmpDeeply() won't call Fatal() if failure
// t = t.FailureIsFatal(false)
// t.CmpDeeply(...)
//
// or, if only one call is critic:
//
// // This CmpDeeply() call will call Fatal() if failure
// t.FailureIsFatal().CmpDeeply(...)
// // Following t.CmpDeeply() won't call Fatal() if failure
// t.CmpDeeply(...)
// t.CmpDeeply(...)
func (t *T) FailureIsFatal(enable ...bool) *T {
new := *t
new.Config.FailureIsFatal = len(enable) == 0 || enable[0]
return &new
}

// CmpDeeply is mostly a shortcut for:
//
// CmpDeeply(t.TestingFT, got, expected, args...)
Expand Down
36 changes: 36 additions & 0 deletions t_struct_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,3 +200,39 @@ func TestRun(tt *testing.T) {
t.True(ok)
t.True(runPassed)
}

func TestFailureIsFatal(tt *testing.T) {
ttt := &TestTestingFT{}

// All t.True(false) tests of course fail

// Using default config
t := NewT(ttt)
t.True(false) // failure
CmpNotEmpty(tt, ttt.LastMessage)
CmpFalse(tt, ttt.IsFatal, "by default it not fatal")

// Using specific config
t = NewT(ttt, ContextConfig{FailureIsFatal: true})
t.True(false) // failure
CmpNotEmpty(tt, ttt.LastMessage)
CmpTrue(tt, ttt.IsFatal, "it must be fatal")

// Using FailureIsFatal()
t = NewT(ttt).FailureIsFatal()
t.True(false) // failure
CmpNotEmpty(tt, ttt.LastMessage)
CmpTrue(tt, ttt.IsFatal, "it must be fatal")

// Using FailureIsFatal(true)
t = NewT(ttt).FailureIsFatal(true)
t.True(false) // failure
CmpNotEmpty(tt, ttt.LastMessage)
CmpTrue(tt, ttt.IsFatal, "it must be fatal")

// Canceling specific config
t = NewT(ttt, ContextConfig{FailureIsFatal: false}).FailureIsFatal(false)
t.True(false) // failure
CmpNotEmpty(tt, ttt.LastMessage)
CmpFalse(tt, ttt.IsFatal, "it must be not fatal")
}
Loading

0 comments on commit f246493

Please sign in to comment.