Skip to content

Commit

Permalink
Handle Fail calls on the TestingT in the right place
Browse files Browse the repository at this point in the history
  • Loading branch information
mrsheepuk committed Aug 18, 2023
1 parent 29e34b6 commit 774cad0
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 29 deletions.
19 changes: 5 additions & 14 deletions suite.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,11 @@ func (s *suite) runStep(ctx context.Context, pickle *Scenario, step *Step, scena

earlyReturn := scenarioErr != nil || err == ErrUndefined

// Check for any calls to Fail on dogT
if err == nil {
err = getDogTestingT(ctx).isFailed()
}

switch {
case errors.Is(err, ErrPending):
sr.Status = StepPending
Expand Down Expand Up @@ -532,18 +537,12 @@ func (s *suite) runPickle(pickle *messages.Pickle) (err error) {
s.testingT.Run(pickle.Name, func(t *testing.T) {
dt.t = t
ctx, err = s.runSteps(ctx, pickle, pickle.Steps)
if err == nil {
err = dt.check()
}
if s.shouldFail(err) {
t.Errorf("%+v", err)
}
})
} else {
ctx, err = s.runSteps(ctx, pickle, pickle.Steps)
if err == nil {
err = dt.check()
}
}

// After scenario handlers are called in context of last evaluated step
Expand All @@ -552,14 +551,6 @@ func (s *suite) runPickle(pickle *messages.Pickle) (err error) {
return err
}

type TestingT interface {
Log(args ...interface{})
Logf(format string, args ...interface{})
Errorf(format string, args ...interface{})
Fail()
FailNow()
}

// Logf will log test output. If called in the context of a test and testing.T has been registered,
// this will log using the step's testing.T, else it will simply log to stdout.
func Logf(ctx context.Context, format string, args ...interface{}) {
Expand Down
50 changes: 35 additions & 15 deletions testingt.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,33 @@ package godog
import (
"context"
"fmt"
"strings"
"testing"
)

type TestingT interface {
Log(args ...interface{})
Logf(format string, args ...interface{})
Errorf(format string, args ...interface{})
Fail()
FailNow()
}

// GetTestingT returns a TestingT compatible interface from the current test context. It will return
// nil if called outside the context of a test. This can be used with (for example) testify's assert
// and require packages.
func GetTestingT(ctx context.Context) TestingT {
return getDogTestingT(ctx)

Check warning on line 22 in testingt.go

View check run for this annotation

Codecov / codecov/patch

testingt.go#L21-L22

Added lines #L21 - L22 were not covered by tests
}

// errFailNow should be returned inside a panic within the test to immediately halt execution of that
// test
var errFailNow = fmt.Errorf("FailNow called")

type dogTestingT struct {
t *testing.T
failed bool
t *testing.T
failed bool
failMessages []string
}

// check interface:
Expand All @@ -36,41 +53,44 @@ func (dt *dogTestingT) Logf(format string, args ...interface{}) {

func (dt *dogTestingT) Errorf(format string, args ...interface{}) {
dt.Logf(format, args...)
dt.failed = true
dt.failMessages = append(dt.failMessages, fmt.Sprintf(format, args...))
dt.Fail()

Check warning on line 57 in testingt.go

View check run for this annotation

Codecov / codecov/patch

testingt.go#L54-L57

Added lines #L54 - L57 were not covered by tests
}

func (dt *dogTestingT) Fail() {
dt.failed = true

Check warning on line 61 in testingt.go

View check run for this annotation

Codecov / codecov/patch

testingt.go#L60-L61

Added lines #L60 - L61 were not covered by tests
}

func (dt *dogTestingT) FailNow() {
dt.Fail()
panic(errFailNow)

Check warning on line 66 in testingt.go

View check run for this annotation

Codecov / codecov/patch

testingt.go#L64-L66

Added lines #L64 - L66 were not covered by tests
}

func (dt *dogTestingT) check() error {
if dt.failed {
return fmt.Errorf("one or more checks failed")
// isFailed will return an error representing the calls to Fail made during this test
func (dt *dogTestingT) isFailed() error {
if !dt.failed {
return nil
}
switch len(dt.failMessages) {
case 0:
return fmt.Errorf("fail called on TestingT")
case 1:
return fmt.Errorf(dt.failMessages[0])
default:
return fmt.Errorf("checks failed:\n* %s", strings.Join(dt.failMessages, "\n* "))

Check warning on line 80 in testingt.go

View check run for this annotation

Codecov / codecov/patch

testingt.go#L74-L80

Added lines #L74 - L80 were not covered by tests
}

return nil
}

type testingTCtxVal struct{}

func setContextDogTester(ctx context.Context, dt *dogTestingT) context.Context {
return context.WithValue(ctx, testingTCtxVal{}, dt)
}

func getDogTestingT(ctx context.Context) *dogTestingT {
dt, ok := ctx.Value(testingTCtxVal{}).(*dogTestingT)
if !ok {
return nil
}

Check warning on line 94 in testingt.go

View check run for this annotation

Codecov / codecov/patch

testingt.go#L93-L94

Added lines #L93 - L94 were not covered by tests
return dt
}

// GetTestingT returns a TestingT compatible interface from the current test context. It will return
// nil if called outside the context of a test. This can be used with (for example) testify's assert
// and require packages.
func GetTestingT(ctx context.Context) TestingT {
return getDogTestingT(ctx)
}

0 comments on commit 774cad0

Please sign in to comment.