Provide a useful implementation of something compatible with testing.T (#571)

* Attempting to provide a useful implementation of something compatible with testing.T

* Handle Fail calls on the TestingT in the right place

* Provide as much of testing.T as possible + tidy up

* Add initial tests for testingT support

* Check compatibility with testing.T and friends

Co-authored-by: Piotr Bocheński <bochenski.piotr@gmail.com>

* Update assert-godogs example to show new usage. Rename 'GetTestingT(ctx)' to 'T(ctx)'

* Update changelog and readme with new usage

* Improve test coverage

* Review updates

---------

Co-authored-by: Piotr Bocheński <bochenski.piotr@gmail.com>
Этот коммит содержится в:
Mark Hughes 2024-04-29 09:26:25 +01:00 коммит произвёл GitHub
родитель 10407bc5a9
коммит 7017c73ef8
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
12 изменённых файлов: 562 добавлений и 90 удалений

Просмотреть файл

@ -8,6 +8,9 @@ This document is formatted according to the principles of [Keep A CHANGELOG](htt
## Unreleased
### Added
- Provide testing.T-compatible interface on test context, allowing usage of assertion libraries such as testify's assert/require - ([571](https://github.com/cucumber/godog/pull/571) - [mrsheepuk](https://github.com/mrsheepuk))
## [v0.14.0]
### Added
- Improve ErrSkip handling, add test for Summary and operations order ([584](https://github.com/cucumber/godog/pull/584) - [vearutop](https://github.com/vearutop))

Просмотреть файл

@ -495,31 +495,12 @@ If you want to filter scenarios by tags, you can use the `-t=<expression>` or `-
A more extensive example can be [found here](/_examples/assert-godogs).
```go
func thereShouldBeRemaining(remaining int) error {
return assertExpectedAndActual(
assert.Equal, Godogs, remaining,
func thereShouldBeRemaining(ctx context.Context, remaining int) error {
assert.Equal(
godog.T(ctx), Godogs, remaining,
"Expected %d godogs to be remaining, but there is %d", remaining, Godogs,
)
}
// assertExpectedAndActual is a helper function to allow the step function to call
// assertion functions where you want to compare an expected and an actual value.
func assertExpectedAndActual(a expectedAndActualAssertion, expected, actual interface{}, msgAndArgs ...interface{}) error {
var t asserter
a(&t, expected, actual, msgAndArgs...)
return t.err
}
type expectedAndActualAssertion func(t assert.TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool
// asserter is used to be able to retrieve the error reported by the called assertion
type asserter struct {
err error
}
// Errorf is used by the called assertion to report an error
func (a *asserter) Errorf(format string, args ...interface{}) {
a.err = fmt.Errorf(format, args...)
return nil
}
```

Просмотреть файл

@ -2,7 +2,6 @@ package main
import (
"context"
"fmt"
"os"
"testing"
@ -36,31 +35,22 @@ func thereAreGodogs(available int) error {
return nil
}
func iEat(num int) error {
err := assertExpectedAndActual(
assert.GreaterOrEqual, Godogs, num,
"You cannot eat %d godogs, there are %d available", num, Godogs,
)
if err != nil {
return err
func iEat(ctx context.Context, num int) error {
if !assert.GreaterOrEqual(godog.T(ctx), Godogs, num, "You cannot eat %d godogs, there are %d available", num, Godogs) {
return nil
}
Godogs -= num
return nil
}
func thereShouldBeRemaining(remaining int) error {
return assertExpectedAndActual(
assert.Equal, Godogs, remaining,
"Expected %d godogs to be remaining, but there is %d", remaining, Godogs,
)
func thereShouldBeRemaining(ctx context.Context, remaining int) error {
assert.Equal(godog.T(ctx), Godogs, remaining, "Expected %d godogs to be remaining, but there is %d", remaining, Godogs)
return nil
}
func thereShouldBeNoneRemaining() error {
return assertActual(
assert.Empty, Godogs,
"Expected none godogs to be remaining, but there is %d", Godogs,
)
func thereShouldBeNoneRemaining(ctx context.Context) error {
assert.Empty(godog.T(ctx), Godogs, "Expected none godogs to be remaining, but there is %d", Godogs)
return nil
}
func InitializeScenario(ctx *godog.ScenarioContext) {
@ -74,34 +64,3 @@ func InitializeScenario(ctx *godog.ScenarioContext) {
ctx.Step(`^there should be (\d+) remaining$`, thereShouldBeRemaining)
ctx.Step(`^there should be none remaining$`, thereShouldBeNoneRemaining)
}
// assertExpectedAndActual is a helper function to allow the step function to call
// assertion functions where you want to compare an expected and an actual value.
func assertExpectedAndActual(a expectedAndActualAssertion, expected, actual interface{}, msgAndArgs ...interface{}) error {
var t asserter
a(&t, expected, actual, msgAndArgs...)
return t.err
}
type expectedAndActualAssertion func(t assert.TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool
// assertActual is a helper function to allow the step function to call
// assertion functions where you want to compare an actual value to a
// predined state like nil, empty or true/false.
func assertActual(a actualAssertion, actual interface{}, msgAndArgs ...interface{}) error {
var t asserter
a(&t, actual, msgAndArgs...)
return t.err
}
type actualAssertion func(t assert.TestingT, actual interface{}, msgAndArgs ...interface{}) bool
// asserter is used to be able to retrieve the error reported by the called assertion
type asserter struct {
err error
}
// Errorf is used by the called assertion to report an error
func (a *asserter) Errorf(format string, args ...interface{}) {
a.err = fmt.Errorf(format, args...)
}

Просмотреть файл

@ -13,7 +13,7 @@ Feature: event stream formatter
"""
Scenario: should process simple scenario
Given a feature path "features/load.feature:26"
Given a feature path "features/load.feature:27"
When I run feature suite with formatter "events"
Then the following events should be fired:
"""
@ -34,7 +34,7 @@ Feature: event stream formatter
"""
Scenario: should process outline scenario
Given a feature path "features/load.feature:34"
Given a feature path "features/load.feature:35"
When I run feature suite with formatter "events"
Then the following events should be fired:
"""

Просмотреть файл

@ -350,7 +350,7 @@ Feature: pretty formatter
Scenario: Should scenarios identified with path:line and preserve the order.
Given a feature path "features/load.feature:6"
And a feature path "features/multistep.feature:6"
And a feature path "features/load.feature:26"
And a feature path "features/load.feature:27"
And a feature path "features/multistep.feature:23"
When I run feature suite with formatter "pretty"
Then the rendered output will be as follows:
@ -363,7 +363,7 @@ Feature: pretty formatter
Scenario: load features within path # features/load.feature:6
Given a feature path "features" # suite_context_test.go:0 -> *godogFeaturesScenario
When I parse features # suite_context_test.go:0 -> *godogFeaturesScenario
Then I should have 13 feature files: # suite_context_test.go:0 -> *godogFeaturesScenario
Then I should have 14 feature files: # suite_context_test.go:0 -> *godogFeaturesScenario
\"\"\"
features/background.feature
features/events.feature
@ -378,6 +378,7 @@ Feature: pretty formatter
features/run.feature
features/snippets.feature
features/tags.feature
features/testingt.feature
\"\"\"
Feature: run features with nested steps
@ -407,7 +408,7 @@ Feature: pretty formatter
As a test suite
I need to be able to load features
Scenario: load a specific feature file # features/load.feature:26
Scenario: load a specific feature file # features/load.feature:27
Given a feature path "features/load.feature" # suite_context_test.go:0 -> *godogFeaturesScenario
When I parse features # suite_context_test.go:0 -> *godogFeaturesScenario
Then I should have 1 feature file: # suite_context_test.go:0 -> *godogFeaturesScenario

Просмотреть файл

@ -8,7 +8,7 @@ Savybė: užkrauti savybes
Scenarijus: savybių užkrovimas iš aplanko
Duota savybių aplankas "features"
Kai aš išskaitau savybes
Tada aš turėčiau turėti 13 savybių failus:
Tada aš turėčiau turėti 14 savybių failus:
"""
features/background.feature
features/events.feature
@ -23,4 +23,5 @@ Savybė: užkrauti savybes
features/run.feature
features/snippets.feature
features/tags.feature
features/testingt.feature
"""

Просмотреть файл

@ -6,7 +6,7 @@ Feature: load features
Scenario: load features within path
Given a feature path "features"
When I parse features
Then I should have 13 feature files:
Then I should have 14 feature files:
"""
features/background.feature
features/events.feature
@ -21,6 +21,7 @@ Feature: load features
features/run.feature
features/snippets.feature
features/tags.feature
features/testingt.feature
"""
Scenario: load a specific feature file

194
features/testingt.feature Обычный файл
Просмотреть файл

@ -0,0 +1,194 @@
Feature: providing testingT compatibility
In order to test application behavior using standard go assertion techniques
As a test suite
I need to be able to provide a testing.T compatible interface
Scenario Outline: should fail test with no message if <op> called on testing T
Given a feature "failed.feature" file:
"""
Feature: failed feature
Scenario: fail a scenario
Given passing step
When my step fails the test by calling <op> on testing T
"""
When I run feature suite
Then the suite should have failed
And the following steps should be passed:
"""
passing step
"""
And the following step should be failed:
"""
my step fails the test by calling <op> on testing T
"""
Examples:
| op |
| Fail |
| FailNow |
Scenario Outline: should fail test with message if <op> called on T
Given a feature "failed.feature" file:
"""
Feature: failed feature
Scenario: fail a scenario
Given passing step
When my step fails the test by calling <op> on testing T with message "an unformatted message"
"""
When I run feature suite
Then the suite should have failed
And the following steps should be passed:
"""
passing step
"""
And the following step should be failed:
"""
my step fails the test by calling <op> on testing T with message "an unformatted message"
"""
Examples:
| op |
| Error |
| Fatal |
Scenario Outline: should fail test with formatted message if <op> called on T
Given a feature "failed.feature" file:
"""
Feature: failed feature
Scenario: fail a scenario
Given passing step
When my step fails the test by calling <op> on testing T with message "a formatted message %s" and argument "arg1"
"""
When I run feature suite
Then the suite should have failed
And the following steps should be passed:
"""
passing step
"""
And the following step should be failed:
"""
my step fails the test by calling <op> on testing T with message "a formatted message %s" and argument "arg1"
"""
Examples:
| op |
| Errorf |
| Fatalf |
Scenario: should pass test when testify assertions pass
Given a feature "testify.feature" file:
"""
Feature: passed feature
Scenario: pass a scenario
Given passing step
When my step calls testify's assert.Equal with expected "exp" and actual "exp"
When my step calls testify's require.Equal with expected "exp" and actual "exp"
"""
When I run feature suite
Then the suite should have passed
And the following steps should be passed:
"""
passing step
my step calls testify's assert.Equal with expected "exp" and actual "exp"
my step calls testify's require.Equal with expected "exp" and actual "exp"
"""
Scenario: should fail test when testify assertions do not pass
Given a feature "testify.feature" file:
"""
Feature: failed feature
Scenario: fail a scenario
Given passing step
When my step calls testify's assert.Equal with expected "exp" and actual "not"
And my step calls testify's assert.Equal with expected "exp2" and actual "not"
"""
When I run feature suite
Then the suite should have failed
And the following steps should be passed:
"""
passing step
"""
And the following steps should be failed:
"""
my step calls testify's assert.Equal with expected "exp" and actual "not"
"""
And the following steps should be skipped:
"""
my step calls testify's assert.Equal with expected "exp2" and actual "not"
"""
Scenario: should fail test when multiple testify assertions are used in a step
Given a feature "testify.feature" file:
"""
Feature: failed feature
Scenario: fail a scenario
Given passing step
When my step calls testify's assert.Equal 3 times
"""
When I run feature suite
Then the suite should have failed
And the following steps should be passed:
"""
passing step
"""
And the following steps should be failed:
"""
my step calls testify's assert.Equal 3 times
"""
Scenario: should pass test when multiple testify assertions are used successfully in a step
Given a feature "testify.feature" file:
"""
Feature: passed feature
Scenario: pass a scenario
Given passing step
When my step calls testify's assert.Equal 3 times with match
"""
When I run feature suite
Then the suite should have passed
And the following steps should be passed:
"""
passing step
my step calls testify's assert.Equal 3 times with match
"""
Scenario Outline: should skip test when <op> is called on the testing.T
Given a feature "testify.feature" file:
"""
Feature: skipped feature
Scenario: skip a scenario
Given passing step
When my step skips the test by calling <op> on testing T
"""
When I run feature suite
Then the suite should have passed
And the following steps should be passed:
"""
passing step
"""
And the following steps should be skipped:
"""
my step skips the test by calling <op> on testing T
"""
Examples:
| op |
| Skip |
| SkipNow |
Scenario: should log when Logf/Log called on testing.T
When my step calls Logf on testing T with message "format this %s" and argument "formatparam1"
And my step calls Log on testing T with message "log this message"
Then the logged messages should include "format this formatparam1"
And the logged messages should include "log this message"
Scenario: should log when godog.Logf/Log called
When my step calls godog.Logf with message "format this %s" and argument "formatparam1"
And my step calls godog.Log with message "log this message"
Then the logged messages should include "format this formatparam1"
And the logged messages should include "log this message"

Просмотреть файл

@ -525,11 +525,12 @@ func Test_AllFeaturesRun(t *testing.T) {
...................................................................... 210
...................................................................... 280
...................................................................... 350
...... 356
...................................................................... 420
... 423
94 scenarios (94 passed)
356 steps (356 passed)
108 scenarios (108 passed)
423 steps (423 passed)
0s
`
@ -553,11 +554,12 @@ func Test_AllFeaturesRunAsSubtests(t *testing.T) {
...................................................................... 210
...................................................................... 280
...................................................................... 350
...... 356
...................................................................... 420
... 423
94 scenarios (94 passed)
356 steps (356 passed)
108 scenarios (108 passed)
423 steps (423 passed)
0s
`

Просмотреть файл

@ -85,12 +85,18 @@ func (s *suite) runStep(ctx context.Context, pickle *Scenario, step *Step, scena
// user multistep definitions may panic
defer func() {
if e := recover(); e != nil {
if err != nil {
pe, isErr := e.(error)
switch {
case isErr && errors.Is(pe, errStopNow):
// FailNow or SkipNow called on dogTestingT, so clear the error to let the normal
// below getTestingT(ctx).isFailed() call handle the reasons.
err = nil
case err != nil:
err = &traceError{
msg: fmt.Sprintf("%s: %v", err.Error(), e),
stack: callStack(),
}
} else {
default:
err = &traceError{
msg: fmt.Sprintf("%v", e),
stack: callStack(),
@ -100,6 +106,11 @@ func (s *suite) runStep(ctx context.Context, pickle *Scenario, step *Step, scena
earlyReturn := scenarioErr != nil || errors.Is(err, ErrUndefined)
// Check for any calls to Fail on dogT
if err == nil {
err = getTestingT(ctx).isFailed()
}
switch {
case errors.Is(err, ErrPending):
status = StepPending
@ -509,10 +520,15 @@ func (s *suite) runPickle(pickle *messages.Pickle) (err error) {
s.fmt.Pickle(pickle)
dt := &testingT{
name: pickle.Name,
}
ctx = setContextTestingT(ctx, dt)
// scenario
if s.testingT != nil {
// Running scenario as a subtest.
s.testingT.Run(pickle.Name, func(t *testing.T) {
dt.t = t
ctx, err = s.runSteps(ctx, pickle, pickle.Steps)
if s.shouldFail(err) {
t.Errorf("%+v", err)

Просмотреть файл

@ -161,6 +161,19 @@ func InitializeScenario(ctx *ScenarioContext) {
}
})
// introduced to test testingT
ctx.Step(`^my step (?:fails|skips) the test by calling (FailNow|Fail|SkipNow|Skip) on testing T$`, tc.myStepCallsTFailErrorSkip)
ctx.Step(`^my step fails the test by calling (Fatal|Error) on testing T with message "([^"]*)"$`, tc.myStepCallsTErrorFatal)
ctx.Step(`^my step fails the test by calling (Fatalf|Errorf) on testing T with message "([^"]*)" and argument "([^"]*)"$`, tc.myStepCallsTErrorfFatalf)
ctx.Step(`^my step calls Log on testing T with message "([^"]*)"$`, tc.myStepCallsTLog)
ctx.Step(`^my step calls Logf on testing T with message "([^"]*)" and argument "([^"]*)"$`, tc.myStepCallsTLogf)
ctx.Step(`^my step calls testify's assert.Equal with expected "([^"]*)" and actual "([^"]*)"$`, tc.myStepCallsTestifyAssertEqual)
ctx.Step(`^my step calls testify's require.Equal with expected "([^"]*)" and actual "([^"]*)"$`, tc.myStepCallsTestifyRequireEqual)
ctx.Step(`^my step calls testify's assert.Equal ([0-9]+) times(| with match)$`, tc.myStepCallsTestifyAssertEqualMultipleTimes)
ctx.Step(`^my step calls godog.Log with message "([^"]*)"$`, tc.myStepCallsDogLog)
ctx.Step(`^my step calls godog.Logf with message "([^"]*)" and argument "([^"]*)"$`, tc.myStepCallsDogLogf)
ctx.Step(`^the logged messages should include "([^"]*)"$`, tc.theLoggedMessagesShouldInclude)
ctx.StepContext().Before(tc.inject)
}
@ -385,6 +398,101 @@ func (tc *godogFeaturesScenario) iShouldSeeTheContextInTheNextStep(ctx context.C
return nil
}
func (tc *godogFeaturesScenario) myStepCallsTFailErrorSkip(ctx context.Context, op string) error {
switch op {
case "FailNow":
T(ctx).FailNow()
case "Fail":
T(ctx).Fail()
case "SkipNow":
T(ctx).SkipNow()
case "Skip":
T(ctx).Skip()
default:
return fmt.Errorf("operation %s not supported by iCallTFailErrorSkip", op)
}
return nil
}
func (tc *godogFeaturesScenario) myStepCallsTErrorFatal(ctx context.Context, op string, message string) error {
switch op {
case "Error":
T(ctx).Error(message)
case "Fatal":
T(ctx).Fatal(message)
default:
return fmt.Errorf("operation %s not supported by iCallTErrorFatal", op)
}
return nil
}
func (tc *godogFeaturesScenario) myStepCallsTErrorfFatalf(ctx context.Context, op string, message string, arg string) error {
switch op {
case "Errorf":
T(ctx).Errorf(message, arg)
case "Fatalf":
T(ctx).Fatalf(message, arg)
default:
return fmt.Errorf("operation %s not supported by iCallTErrorfFatalf", op)
}
return nil
}
func (tc *godogFeaturesScenario) myStepCallsTestifyAssertEqual(ctx context.Context, a string, b string) error {
assert.Equal(T(ctx), a, b)
return nil
}
func (tc *godogFeaturesScenario) myStepCallsTestifyAssertEqualMultipleTimes(ctx context.Context, times string, withMatch string) error {
timesInt, err := strconv.Atoi(times)
if err != nil {
return fmt.Errorf("test step has invalid times value %s: %w", times, err)
}
for i := 0; i < timesInt; i++ {
if withMatch == " with match" {
assert.Equal(T(ctx), fmt.Sprintf("exp%v", i), fmt.Sprintf("exp%v", i))
} else {
assert.Equal(T(ctx), "exp", fmt.Sprintf("notexp%v", i))
}
}
return nil
}
func (tc *godogFeaturesScenario) myStepCallsTestifyRequireEqual(ctx context.Context, a string, b string) error {
require.Equal(T(ctx), a, b)
return nil
}
func (tc *godogFeaturesScenario) myStepCallsTLog(ctx context.Context, message string) error {
T(ctx).Log(message)
return nil
}
func (tc *godogFeaturesScenario) myStepCallsTLogf(ctx context.Context, message string, arg string) error {
T(ctx).Logf(message, arg)
return nil
}
func (tc *godogFeaturesScenario) myStepCallsDogLog(ctx context.Context, message string) error {
Log(ctx, message)
return nil
}
func (tc *godogFeaturesScenario) myStepCallsDogLogf(ctx context.Context, message string, arg string) error {
Logf(ctx, message, arg)
return nil
}
func (tc *godogFeaturesScenario) theLoggedMessagesShouldInclude(ctx context.Context, message string) error {
messages := LoggedMessages(ctx)
for _, m := range messages {
if strings.Contains(m, message) {
return nil
}
}
return fmt.Errorf("the message %q was not logged (logged messages: %v)", message, messages)
}
func (tc *godogFeaturesScenario) followingStepsShouldHave(status string, steps *DocString) error {
var expected = strings.Split(steps.Content, "\n")
var actual, unmatched, matched []string

206
testingt.go Обычный файл
Просмотреть файл

@ -0,0 +1,206 @@
package godog
import (
"context"
"fmt"
"strings"
"testing"
)
// T 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 T(ctx context.Context) TestingT {
return getTestingT(ctx)
}
// TestingT is a subset of the public methods implemented by go's testing.T. It allows assertion
// libraries to be used with godog, provided they depend only on this subset of methods.
type TestingT interface {
// Name returns the name of the current pickle under test
Name() string
// Log will log to the current testing.T log if set, otherwise it will log to stdout
Log(args ...interface{})
// Logf will log a formatted string to the current testing.T log if set, otherwise it will log
// to stdout
Logf(format string, args ...interface{})
// Error fails the current test and logs the provided arguments. Equivalent to calling Log then
// Fail.
Error(args ...interface{})
// Errorf fails the current test and logs the formatted message. Equivalent to calling Logf then
// Fail.
Errorf(format string, args ...interface{})
// Fail marks the current test as failed, but does not halt execution of the step.
Fail()
// FailNow marks the current test as failed and halts execution of the step.
FailNow()
// Fatal logs the provided arguments, marks the test as failed and halts execution of the step.
Fatal(args ...interface{})
// Fatal logs the formatted message, marks the test as failed and halts execution of the step.
Fatalf(format string, args ...interface{})
// Skip logs the provided arguments and marks the test as skipped but does not halt execution
// of the step.
Skip(args ...interface{})
// Skipf logs the formatted message and marks the test as skipped but does not halt execution
// of the step.
Skipf(format string, args ...interface{})
// SkipNow marks the current test as skipped and halts execution of the step.
SkipNow()
// Skipped returns true if the test has been marked as skipped.
Skipped() bool
}
// 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{}) {
if t := getTestingT(ctx); t != nil {
t.Logf(format, args...)
return
}
fmt.Printf(format+"\n", args...)
}
// Log 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 Log(ctx context.Context, args ...interface{}) {
if t := getTestingT(ctx); t != nil {
t.Log(args...)
return
}
fmt.Println(args...)
}
// LoggedMessages returns an array of any logged messages that have been recorded during the test
// through calls to godog.Log / godog.Logf or via operations against godog.T(ctx)
func LoggedMessages(ctx context.Context) []string {
if t := getTestingT(ctx); t != nil {
return t.logMessages
}
return nil
}
// errStopNow should be returned inside a panic within the test to immediately halt execution of that
// test
var errStopNow = fmt.Errorf("FailNow or SkipNow called")
type testingT struct {
name string
t *testing.T
failed bool
skipped bool
failMessages []string
logMessages []string
}
// check interface against our testingT and the upstream testing.B/F/T:
var (
_ TestingT = &testingT{}
_ TestingT = (*testing.T)(nil)
)
func (dt *testingT) Name() string {
if dt.t != nil {
return dt.t.Name()
}
return dt.name
}
func (dt *testingT) Log(args ...interface{}) {
dt.logMessages = append(dt.logMessages, fmt.Sprint(args...))
if dt.t != nil {
dt.t.Log(args...)
return
}
fmt.Println(args...)
}
func (dt *testingT) Logf(format string, args ...interface{}) {
dt.logMessages = append(dt.logMessages, fmt.Sprintf(format, args...))
if dt.t != nil {
dt.t.Logf(format, args...)
return
}
fmt.Printf(format+"\n", args...)
}
func (dt *testingT) Error(args ...interface{}) {
dt.Log(args...)
dt.failMessages = append(dt.failMessages, fmt.Sprintln(args...))
dt.Fail()
}
func (dt *testingT) Errorf(format string, args ...interface{}) {
dt.Logf(format, args...)
dt.failMessages = append(dt.failMessages, fmt.Sprintf(format, args...))
dt.Fail()
}
func (dt *testingT) Fail() {
dt.failed = true
}
func (dt *testingT) FailNow() {
dt.Fail()
panic(errStopNow)
}
func (dt *testingT) Fatal(args ...interface{}) {
dt.Log(args...)
dt.FailNow()
}
func (dt *testingT) Fatalf(format string, args ...interface{}) {
dt.Logf(format, args...)
dt.FailNow()
}
func (dt *testingT) Skip(args ...interface{}) {
dt.Log(args...)
dt.skipped = true
}
func (dt *testingT) Skipf(format string, args ...interface{}) {
dt.Logf(format, args...)
dt.skipped = true
}
func (dt *testingT) SkipNow() {
dt.skipped = true
panic(errStopNow)
}
func (dt *testingT) Skipped() bool {
return dt.skipped
}
// isFailed will return an error representing the calls to Fail made during this test
func (dt *testingT) isFailed() error {
if dt.skipped {
return ErrSkip
}
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* "))
}
}
type testingTCtxVal struct{}
func setContextTestingT(ctx context.Context, dt *testingT) context.Context {
return context.WithValue(ctx, testingTCtxVal{}, dt)
}
func getTestingT(ctx context.Context) *testingT {
dt, ok := ctx.Value(testingTCtxVal{}).(*testingT)
if !ok {
return nil
}
return dt
}