diff --git a/features/testingt.feature b/features/testingt.feature index ef845f6c..1125d819 100644 --- a/features/testingt.feature +++ b/features/testingt.feature @@ -3,14 +3,14 @@ Feature: providing testingT compatibility As a test suite I need to be able to provide a testing.T compatible interface - Scenario: should fail test if FailNow called on testing T + Scenario Outline: should fail test with no message if called on testing T Given a feature "failed.feature" file: """ Feature: failed feature Scenario: fail a scenario Given passing step - When I fail the test by calling FailNow on testing T + When my step fails the test by calling on testing T """ When I run feature suite Then the suite should have failed @@ -20,8 +20,61 @@ Feature: providing testingT compatibility """ And the following step should be failed: """ - I fail the test by calling FailNow on testing T + my step fails the test by calling on testing T """ + Examples: + | op | + | Fail | + | FailNow | + + Scenario Outline: should fail test with message if 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 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 on testing T with message "an unformatted message" + """ + Examples: + | op | + | Error | + | Fatal | + + + Scenario Outline: should fail test with formatted message if 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 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 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: @@ -30,16 +83,16 @@ Feature: providing testingT compatibility Scenario: pass a scenario Given passing step - When I call testify's assert.Equal with expected "exp" and actual "exp" - When I call testify's require.Equal with expected "exp" and actual "exp" + 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 - I call testify's assert.Equal with expected "exp" and actual "exp" - I call testify's require.Equal with expected "exp" and actual "exp" + 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 @@ -49,8 +102,8 @@ Feature: providing testingT compatibility Scenario: fail a scenario Given passing step - When I call testify's assert.Equal with expected "exp" and actual "not" - And I call testify's assert.Equal with expected "exp2" and actual "not" + 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 @@ -60,11 +113,11 @@ Feature: providing testingT compatibility """ And the following steps should be failed: """ - I call testify's assert.Equal with expected "exp" and actual "not" + my step calls testify's assert.Equal with expected "exp" and actual "not" """ And the following steps should be skipped: """ - I call testify's assert.Equal with expected "exp2" and actual "not" + 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 @@ -74,7 +127,7 @@ Feature: providing testingT compatibility Scenario: fail a scenario Given passing step - When I call testify's assert.Equal 3 times + When my step calls testify's assert.Equal 3 times """ When I run feature suite Then the suite should have failed @@ -84,7 +137,7 @@ Feature: providing testingT compatibility """ And the following steps should be failed: """ - I call testify's assert.Equal 3 times + my step calls testify's assert.Equal 3 times """ Scenario: should pass test when multiple testify assertions are used successfully in a step @@ -94,24 +147,24 @@ Feature: providing testingT compatibility Scenario: pass a scenario Given passing step - When I call testify's assert.Equal 3 times with match + 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 - I call testify's assert.Equal 3 times with match + my step calls testify's assert.Equal 3 times with match """ - Scenario: should skip test when skip is called on the testing.T + Scenario Outline: should skip test when is called on the testing.T Given a feature "testify.feature" file: """ Feature: skipped feature Scenario: skip a scenario Given passing step - When I skip the test by calling Skip on testing T + When my step skips the test by calling on testing T """ When I run feature suite Then the suite should have passed @@ -121,24 +174,21 @@ Feature: providing testingT compatibility """ And the following steps should be skipped: """ - I skip the test by calling Skip on testing T + my step skips the test by calling on testing T """ + Examples: + | op | + | Skip | + | SkipNow | - Scenario: should log to testing.T - Given a feature "logging.feature" file: - """ - Feature: logged feature + 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: logged scenario - Given passing step - When I call Logf on testing T with message "format this %s" and argument "formatparam1" - And I call Log on testing T with message "log this message" - """ - When I run feature suite - Then the suite should have passed - And the following steps should be passed: - """ - passing step - I call Logf on testing T with message "format this %s" and argument "formatparam1" - I call Log on testing T with message "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" diff --git a/out.tt b/out.tt new file mode 100644 index 00000000..4f9e3283 --- /dev/null +++ b/out.tt @@ -0,0 +1 @@ +ok github.com/cucumber/godog 0.948s diff --git a/run_test.go b/run_test.go index 5d7a11b3..6a070d68 100644 --- a/run_test.go +++ b/run_test.go @@ -525,11 +525,12 @@ func Test_AllFeaturesRun(t *testing.T) { ...................................................................... 210 ...................................................................... 280 ...................................................................... 350 -....................................... 389 +...................................................................... 420 +... 423 -101 scenarios (101 passed) -389 steps (389 passed) +108 scenarios (108 passed) +423 steps (423 passed) 0s ` @@ -553,11 +554,12 @@ func Test_AllFeaturesRunAsSubtests(t *testing.T) { ...................................................................... 210 ...................................................................... 280 ...................................................................... 350 -....................................... 389 +...................................................................... 420 +... 423 -101 scenarios (101 passed) -389 steps (389 passed) +108 scenarios (108 passed) +423 steps (423 passed) 0s ` diff --git a/suite_context_test.go b/suite_context_test.go index b625de65..5217f674 100644 --- a/suite_context_test.go +++ b/suite_context_test.go @@ -162,18 +162,17 @@ func InitializeScenario(ctx *ScenarioContext) { }) // introduced to test testingT - ctx.Step(`^I fail the test by calling FailNow on testing T$`, tc.iCallTFailNow) - ctx.Step(`^I fail the test by calling Fail on testing T$`, tc.iCallTFail) - ctx.Step(`^I fail the test by calling Error on testing T with message "([^"]*)"$`, tc.iCallTError) - ctx.Step(`^I fail the test by calling Errorf on testing T with message "([^"]*)" and argument "([^"]*)"$`, tc.iCallTErrorf) - ctx.Step(`^I skip the test by calling SkipNow on testing T$`, tc.iCallTSkipNow) - ctx.Step(`^I skip the test by calling Skip on testing T$`, tc.iCallTSkip) - ctx.Step(`^I call Logf on testing T with message "([^"]*)" and argument "([^"]*)"$`, tc.iCallTLogf) - ctx.Step(`^I call Log on testing T with message "([^"]*)"$`, tc.iCallTLog) - ctx.Step(`^I call testify's assert.Equal with expected "([^"]*)" and actual "([^"]*)"$`, tc.iCallTestifyAssertEqual) - ctx.Step(`^I call testify's require.Equal with expected "([^"]*)" and actual "([^"]*)"$`, tc.iCallTestifyRequireEqual) - ctx.Step(`^I call testify's assert.Equal ([0-9]+) times$`, tc.iCallTestifyAssertEqualMultipleTimes) - ctx.Step(`^I call testify's assert.Equal ([0-9]+) times with match$`, tc.iCallTestifyAssertEqualMultipleTimesWithMatch) + 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) } @@ -399,90 +398,101 @@ func (tc *godogFeaturesScenario) iShouldSeeTheContextInTheNextStep(ctx context.C return nil } -func (tc *godogFeaturesScenario) iCallTFailNow(ctx context.Context) error { - t := GetTestingT(ctx) - t.FailNow() - return nil -} - -func (tc *godogFeaturesScenario) iCallTFail(ctx context.Context) error { - t := GetTestingT(ctx) - t.Fail() - return nil -} - -func (tc *godogFeaturesScenario) iCallTSkipNow(ctx context.Context) error { - t := GetTestingT(ctx) - t.SkipNow() - return nil -} - -func (tc *godogFeaturesScenario) iCallTSkip(ctx context.Context) error { - t := GetTestingT(ctx) - t.Skip() +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) iCallTError(ctx context.Context, message string) error { - t := GetTestingT(ctx) - t.Error(message) +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) iCallTErrorf(ctx context.Context, message string, arg string) error { - t := GetTestingT(ctx) - t.Errorf(message, arg) +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) iCallTestifyAssertEqual(ctx context.Context, a string, b string) error { - t := GetTestingT(ctx) - assert.Equal(t, a, b) +func (tc *godogFeaturesScenario) myStepCallsTestifyAssertEqual(ctx context.Context, a string, b string) error { + assert.Equal(T(ctx), a, b) return nil } -func (tc *godogFeaturesScenario) iCallTestifyAssertEqualMultipleTimes(ctx context.Context, times string) error { - t := GetTestingT(ctx) +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++ { - assert.Equal(t, "exp", fmt.Sprintf("notexp%v", 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) iCallTestifyAssertEqualMultipleTimesWithMatch(ctx context.Context, times string) error { - t := GetTestingT(ctx) - 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++ { - assert.Equal(t, fmt.Sprintf("exp%v", i), fmt.Sprintf("exp%v", i)) - } +func (tc *godogFeaturesScenario) myStepCallsTestifyRequireEqual(ctx context.Context, a string, b string) error { + require.Equal(T(ctx), a, b) return nil } -func (tc *godogFeaturesScenario) iCallTestifyRequireEqual(ctx context.Context, a string, b string) error { - t := GetTestingT(ctx) - require.Equal(t, a, b) +func (tc *godogFeaturesScenario) myStepCallsTLog(ctx context.Context, message string) error { + T(ctx).Log(message) return nil } -func (tc *godogFeaturesScenario) iCallTLog(ctx context.Context, message string) error { - t := GetTestingT(ctx) - t.Log(message) +func (tc *godogFeaturesScenario) myStepCallsTLogf(ctx context.Context, message string, arg string) error { + T(ctx).Logf(message, arg) return nil } -func (tc *godogFeaturesScenario) iCallTLogf(ctx context.Context, message string, arg string) error { - t := GetTestingT(ctx) - t.Logf(message, arg) +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 diff --git a/testingt.go b/testingt.go index 33d0475d..03b8f727 100644 --- a/testingt.go +++ b/testingt.go @@ -7,11 +7,12 @@ import ( "testing" ) -var ( - _ TestingT = (*testing.B)(nil) - _ TestingT = (*testing.F)(nil) - _ TestingT = (*testing.T)(nil) -) +// 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. @@ -69,11 +70,13 @@ func Log(ctx context.Context, args ...interface{}) { fallbackLog(args...) } -// 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) +// 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 @@ -89,8 +92,13 @@ type testingT struct { logMessages []string } -// check interface: -var _ TestingT = &testingT{} +// check interface against our testingT and the upstream testing.B/F/T: +var ( + _ TestingT = &testingT{} + _ TestingT = (*testing.B)(nil) + _ TestingT = (*testing.F)(nil) + _ TestingT = (*testing.T)(nil) +) func (dt *testingT) Name() string { if dt.t != nil {