diff --git a/DSL.go b/DSL.go index 200c348..6974421 100644 --- a/DSL.go +++ b/DSL.go @@ -16,7 +16,7 @@ import ( // in order to avoid repetitive test cases in the `Then` I often define a `onSuccess` variable, // with a function that takes `testcase#variables` as well and test error return value there with `testcase#variables.T()`. func (spec *Spec) Describe(subjectTopic string, specification sBlock, opts ...SpecOption) { - h(spec.testingTB).Helper() + helper(spec.testingTB).Helper() opts = append([]SpecOption{Group(subjectTopic)}, opts...) spec.Context(fmt.Sprintf(`%s %s`, `describe`, subjectTopic), specification, opts...) } @@ -24,20 +24,20 @@ func (spec *Spec) Describe(subjectTopic string, specification sBlock, opts ...Sp // When is an alias for testcase#Spec.Context // When is used usually to represent `if` based decision reasons about your testing subject. func (spec *Spec) When(desc string, testContextBlock sBlock, opts ...SpecOption) { - h(spec.testingTB).Helper() + helper(spec.testingTB).Helper() spec.Context(fmt.Sprintf(`%s %s`, `when`, desc), testContextBlock, opts...) } // And is an alias for testcase#Spec.Context // And is used to represent additional requirement for reaching a certain testing runtime contexts. func (spec *Spec) And(desc string, testContextBlock sBlock, opts ...SpecOption) { - h(spec.testingTB).Helper() + helper(spec.testingTB).Helper() spec.Context(fmt.Sprintf(`%s %s`, `and`, desc), testContextBlock, opts...) } // Then is an alias for Test func (spec *Spec) Then(desc string, test tBlock, opts ...SpecOption) { - h(spec.testingTB).Helper() + helper(spec.testingTB).Helper() desc = fmt.Sprintf(`%s %s`, `then`, desc) spec.Test(desc, test, opts...) } @@ -60,7 +60,7 @@ func (spec *Spec) Then(desc string, test tBlock, opts ...SpecOption) { // the test specification has side effects that would affect other test specification results, // and, as such, must be executed sequentially. func (spec *Spec) NoSideEffect() { - h(spec.testingTB).Helper() + helper(spec.testingTB).Helper() spec.Parallel() } @@ -73,7 +73,7 @@ func (spec *Spec) NoSideEffect() { // This allows flexibility for the developers to use side effect free variant for local development that has quick feedback loop, // and replace them with the production implementation during CI/CD pipeline which less time critical. func (spec *Spec) HasSideEffect() { - h(spec.testingTB).Helper() + helper(spec.testingTB).Helper() spec.Sequential() } diff --git a/Spec.go b/Spec.go index ceeaa09..c9658cf 100644 --- a/Spec.go +++ b/Spec.go @@ -18,7 +18,7 @@ import ( // NewSpec create new Spec struct that is ready for usage. func NewSpec(tb testing.TB, opts ...SpecOption) *Spec { - h(tb).Helper() + helper(tb).Helper() // tb, opts = checkSuite(tb, opts) var s *Spec switch tb := tb.(type) { @@ -38,7 +38,7 @@ func NewSpec(tb testing.TB, opts ...SpecOption) *Spec { } func newSpec(tb testing.TB, opts ...SpecOption) *Spec { - h(tb).Helper() + helper(tb).Helper() s := &Spec{ testingTB: tb, opts: opts, @@ -53,7 +53,7 @@ func newSpec(tb testing.TB, opts ...SpecOption) *Spec { } func (spec *Spec) newSubSpec(desc string, opts ...SpecOption) *Spec { - h(spec.testingTB).Helper() + helper(spec.testingTB).Helper() spec.immutable = true sub := newSpec(spec.testingTB, opts...) sub.parent = spec @@ -144,14 +144,16 @@ type ( // To verify easily your state-machine, you can count the `if`s in your implementation, // and check that each `if` has 2 `When` block to represent the two possible path. func (spec *Spec) Context(desc string, testContextBlock sBlock, opts ...SpecOption) { + helper(spec.testingTB).Helper() spec.modify(func(spec *Spec) { + helper(spec.testingTB).Helper() + spec.defs = append(spec.defs, func(oth *Spec) { oth.Context(desc, testContextBlock, opts...) }) if spec.isSuite() { return } - h(spec.testingTB).Helper() sub := spec.newSubSpec(desc, opts...) if spec.sync { defer sub.Finish() @@ -203,14 +205,15 @@ func (spec *Spec) Context(desc string, testContextBlock sBlock, opts ...SpecOpti // It should not contain anything that modify the test subject input. // It should focus only on asserting the result of the subject. func (spec *Spec) Test(desc string, test tBlock, opts ...SpecOption) { + helper(spec.testingTB).Helper() spec.modify(func(spec *Spec) { + helper(spec.testingTB).Helper() spec.defs = append(spec.defs, func(oth *Spec) { oth.Test(desc, test, opts...) }) if spec.isSuite() { return } - h(spec.testingTB).Helper() s := spec.newSubSpec(desc, opts...) s.isTest = !s.isBenchmark s.hasRan = true @@ -224,7 +227,9 @@ const panicMessageForRunningBenchmarkAfterTest = `when .Benchmark is defined, th // // Creating a Benchmark will signal the Spec that test and benchmark happens seperately, and a test should not double as a benchmark. func (spec *Spec) Benchmark(desc string, test tBlock, opts ...SpecOption) { + helper(spec.testingTB).Helper() spec.modify(func(spec *Spec) { + helper(spec.testingTB).Helper() if spec.isTestRunner() { return } @@ -248,8 +253,9 @@ const warnEventOnImmutableFormat = `you can't use .%s after you already used whe // Using values from *vars when Parallel is safe. // It is a shortcut for executing *testing.T#Parallel() for each test func (spec *Spec) Parallel() { + helper(spec.testingTB).Helper() spec.modify(func(spec *Spec) { - h(spec.testingTB).Helper() + helper(spec.testingTB).Helper() if spec.immutable { spec.testingTB.Fatalf(warnEventOnImmutableFormat, `Parallel`) } @@ -260,8 +266,9 @@ func (spec *Spec) Parallel() { // SkipBenchmark will flag the current Spec / Context to be skipped during Benchmark mode execution. // If you wish to skip only a certain test, not the whole Spec / Context, use the SkipBenchmark SpecOption instead. func (spec *Spec) SkipBenchmark() { + helper(spec.testingTB).Helper() spec.modify(func(spec *Spec) { - h(spec.testingTB).Helper() + helper(spec.testingTB).Helper() if spec.immutable { spec.testingTB.Fatalf(warnEventOnImmutableFormat, `SkipBenchmark`) } @@ -275,8 +282,9 @@ func (spec *Spec) SkipBenchmark() { // This is useful when you want to create a spec helper package // and there you want to manage if you want to use components side effects or not. func (spec *Spec) Sequential() { + helper(spec.testingTB).Helper() spec.modify(func(spec *Spec) { - h(spec.testingTB).Helper() + helper(spec.testingTB).Helper() if spec.immutable { panic(fmt.Sprintf(warnEventOnImmutableFormat, `Sequential`)) } @@ -300,14 +308,15 @@ func (spec *Spec) Sequential() { // TESTCASE_TAG_EXCLUDE='E2E' go test ./... // TESTCASE_TAG_INCLUDE='E2E' TESTCASE_TAG_EXCLUDE='list,of,excluded,tags' go test ./... func (spec *Spec) Tag(tags ...string) { + helper(spec.testingTB).Helper() spec.modify(func(spec *Spec) { - h(spec.testingTB).Helper() + helper(spec.testingTB).Helper() spec.tags = append(spec.tags, tags...) }) } func (spec *Spec) isAllowedToRun() bool { - h(spec.testingTB).Helper() + helper(spec.testingTB).Helper() if spec.isTest && !spec.isTestAllowedToRun() { return false @@ -337,7 +346,7 @@ func (spec *Spec) isAllowedToRun() bool { } func (spec *Spec) isTestAllowedToRun() bool { - h(spec.testingTB).Helper() + helper(spec.testingTB).Helper() for _, context := range spec.specsFromParent() { if context.skipTest { return false @@ -347,7 +356,7 @@ func (spec *Spec) isTestAllowedToRun() bool { } func (spec *Spec) isBenchAllowedToRun() bool { - h(spec.testingTB).Helper() + helper(spec.testingTB).Helper() for _, context := range spec.specsFromParent() { if context.skipBenchmark { return false @@ -357,7 +366,7 @@ func (spec *Spec) isBenchAllowedToRun() bool { } func (spec *Spec) lookupRetryFlaky() (assert.Retry, bool) { - h(spec.testingTB).Helper() + helper(spec.testingTB).Helper() for _, context := range spec.specsFromParent() { if context.flaky != nil { return *context.flaky, true @@ -367,7 +376,7 @@ func (spec *Spec) lookupRetryFlaky() (assert.Retry, bool) { } func (spec *Spec) lookupRetryEventually() (assert.Retry, bool) { - h(spec.testingTB).Helper() + helper(spec.testingTB).Helper() for _, context := range spec.specsFromParent() { if context.eventually != nil { return *context.eventually, true @@ -377,7 +386,7 @@ func (spec *Spec) lookupRetryEventually() (assert.Retry, bool) { } func (spec *Spec) printDescription(tb testing.TB) { - h(spec.testingTB).Helper() + helper(spec.testingTB).Helper() tb.Helper() var lines []interface{} @@ -416,7 +425,7 @@ func (spec *Spec) name() string { ///////////////////////////////////////////////////////=- run -=//////////////////////////////////////////////////////// func (spec *Spec) run(blk func(*T)) { - h(spec.testingTB).Helper() + helper(spec.testingTB).Helper() if !spec.isAllowedToRun() { return } @@ -427,7 +436,7 @@ func (spec *Spec) run(blk func(*T)) { return } spec.addTest(func() { - if h, ok := tb.(helper); ok { + if h, ok := tb.(testingHelper); ok { h.Helper() } tb.Run(name, func(t *testing.T) { @@ -447,7 +456,7 @@ func (spec *Spec) run(blk func(*T)) { }) case TBRunner: spec.addTest(func() { - if h, ok := tb.(helper); ok { + if h, ok := tb.(testingHelper); ok { h.Helper() } tb.Run(name, func(tb testing.TB) { @@ -457,7 +466,7 @@ func (spec *Spec) run(blk func(*T)) { }) default: spec.addTest(func() { - if h, ok := tb.(helper); ok { + if h, ok := tb.(testingHelper); ok { h.Helper() } spec.runTB(tb, blk) @@ -479,6 +488,7 @@ func (spec *Spec) isTestRunner() bool { func (spec *Spec) modify(blk func(spec *Spec)) { spec.mods = append(spec.mods, blk) if isValidTestingTB(spec.testingTB) { + helper(spec.testingTB).Helper() blk(spec) } } @@ -491,8 +501,9 @@ func (spec *Spec) getTestSeed(tb testing.TB) int64 { } func (spec *Spec) runTB(tb testing.TB, blk func(*T)) { - h(spec.testingTB).Helper() - tb.Helper() + helper(spec.testingTB).Helper() + helper(tb).Helper() + spec.hasRan = true if tb, ok := tb.(interface{ Parallel() }); ok && spec.isParallel() { tb.Parallel() @@ -528,8 +539,9 @@ func (spec *Spec) runTB(tb testing.TB, blk func(*T)) { } func (spec *Spec) runB(b *testing.B, blk func(*T)) { - h(spec.testingTB).Helper() - b.Helper() + helper(spec.testingTB).Helper() + helper(b).Helper() + t := newT(b, spec) if _, ok := spec.lookupRetryFlaky(); ok { b.Skip(`skipping because flaky flag`) @@ -561,6 +573,7 @@ type visitorFunc func(s *Spec) func (fn visitorFunc) Visit(s *Spec) { fn(s) } func (spec *Spec) acceptVisitor(v visitor) { + helper(spec.testingTB).Helper() for _, child := range spec.children { child.acceptVisitor(v) } @@ -573,8 +586,9 @@ func (spec *Spec) acceptVisitor(v visitor) { // Such case can be when a resource leaked inside a testing scope // and resource closed with a deferred function, but the spec is still not ran. func (spec *Spec) Finish() { + helper(spec.testingTB).Helper() spec.modify(func(spec *Spec) { - h(spec.testingTB).Helper() + helper(spec.testingTB).Helper() var tests []func() spec.acceptVisitor(visitorFunc(func(s *Spec) { if s.finished { @@ -605,7 +619,7 @@ func (spec *Spec) documentResults() { if spec.isSuite() || spec.isBenchmark { return } - h(spec.testingTB).Helper() + helper(spec.testingTB).Helper() spec.doc.once.Do(func() { var collect func(*Spec) []doc.TestingCase collect = func(spec *Spec) []doc.TestingCase { @@ -630,7 +644,7 @@ func (spec *Spec) documentResults() { } func (spec *Spec) withFinishUsingTestingTB(tb testing.TB, blk func()) { - h(spec.testingTB).Helper() + helper(spec.testingTB).Helper() tb.Helper() ogTB := spec.testingTB defer func() { spec.testingTB = ogTB }() @@ -640,7 +654,7 @@ func (spec *Spec) withFinishUsingTestingTB(tb testing.TB, blk func()) { } func (spec *Spec) isParallel() bool { - h(spec.testingTB).Helper() + helper(spec.testingTB).Helper() var ( isParallel bool isSequential bool @@ -689,7 +703,7 @@ func (spec *Spec) specsFromCurrent() []*Spec { } func (spec *Spec) lookupParent() (*Spec, bool) { - h(spec.testingTB).Helper() + helper(spec.testingTB).Helper() for _, s := range spec.specsFromCurrent() { if s.hasRan { // skip test continue @@ -703,7 +717,7 @@ func (spec *Spec) lookupParent() (*Spec, bool) { } func (spec *Spec) getTagSet() map[string]struct{} { - h(spec.testingTB).Helper() + helper(spec.testingTB).Helper() tagsSet := make(map[string]struct{}) for _, ctx := range spec.specsFromParent() { for _, tag := range ctx.tags { @@ -716,7 +730,7 @@ func (spec *Spec) getTagSet() map[string]struct{} { // addTest registers a testing block to be executed as part of the Spec. // the main purpose is to enable test execution order manipulation throught the TESTCASE_SEED. func (spec *Spec) addTest(blk func()) { - h(spec.testingTB).Helper() + helper(spec.testingTB).Helper() if p, ok := spec.lookupParent(); ok && p.sync { blk() @@ -744,7 +758,7 @@ To achieve this, the current "testcase.Spec" needs to be created as a suite by p Once the "Spec" is converted into a suite, you can use "testcase.Spec#Spec" as the function block for another "testcase.Spec" "#Context" call.` func (spec *Spec) Spec(oth *Spec) { - h(oth.testingTB).Helper() + helper(oth.testingTB).Helper() if !spec.isSuite() { panic(panicMessageSpecSpec) } @@ -803,7 +817,7 @@ func (suite SpecSuite) run(tb testing.TB) { s.Context(suite.N, suite.Spec, Group(suite.N)) } -func h(tb helper) helper { +func helper(tb testingHelper) testingHelper { if tb == nil { return internal.NullTB{} } diff --git a/Suite.go b/Suite.go index 8365e0f..c96e392 100644 --- a/Suite.go +++ b/Suite.go @@ -34,7 +34,7 @@ type OpenSuite interface { // By using RunSuite, you don't have to distinguish between testing or benchmark execution mod. // It supports *testing.T, *testing.B, *testcase.T, *testcase.Spec and CustomTB test runners. func RunSuite[S Suite, TBS anyTBOrSpec](tb TBS, contracts ...S) { - if tb, ok := any(tb).(helper); ok { + if tb, ok := any(tb).(testingHelper); ok { tb.Helper() } s := ToSpec(tb) @@ -47,7 +47,7 @@ func RunSuite[S Suite, TBS anyTBOrSpec](tb TBS, contracts ...S) { } func RunOpenSuite[OS OpenSuite, TBS anyTBOrSpec](tb TBS, contracts ...OS) { - if tb, ok := any(tb).(helper); ok { + if tb, ok := any(tb).(testingHelper); ok { tb.Helper() } s := ToSpec(tb) diff --git a/TB.go b/TB.go index 025d799..ac7a956 100644 --- a/TB.go +++ b/TB.go @@ -46,7 +46,7 @@ type bRunner interface { Run(string, func(b *testing.B)) bool } -type helper interface { +type testingHelper interface { Helper() } diff --git a/hooks.go b/hooks.go index cc46acd..6537d14 100644 --- a/hooks.go +++ b/hooks.go @@ -30,7 +30,7 @@ type hookOnce struct { // This hook applied to this scope and anything that is nested from here. // All setup block is stackable. func (spec *Spec) Before(beforeBlock tBlock) { - h(spec.testingTB).Helper() + helper(spec.testingTB).Helper() spec.Around(func(t *T) func() { beforeBlock(t) return func() {} @@ -45,7 +45,7 @@ func (spec *Spec) Before(beforeBlock tBlock) { // // DEPRECATED: use Spec.Before with T.Cleanup or Spec.Before with T.Defer instead func (spec *Spec) After(afterBlock tBlock) { - h(spec.testingTB).Helper() + helper(spec.testingTB).Helper() spec.Around(func(t *T) func() { return func() { afterBlock(t) } }) @@ -59,9 +59,10 @@ func (spec *Spec) After(afterBlock tBlock) { // // DEPRECATED: use Spec.Before with T.Cleanup or Spec.Before with T.Defer instead func (spec *Spec) Around(block hookBlock) { - h(spec.testingTB).Helper() + helper(spec.testingTB).Helper() frame, _ := caller.GetFrame() spec.modify(func(spec *Spec) { + helper(spec.testingTB).Helper() if spec.immutable { spec.testingTB.Fatal(hookWarning) } @@ -75,9 +76,11 @@ func (spec *Spec) Around(block hookBlock) { // BeforeAll give you the ability to create a hook // that runs only once before the test cases. func (spec *Spec) BeforeAll(blk func(tb testing.TB)) { - h(spec.testingTB).Helper() + helper(spec.testingTB).Helper() frame, _ := caller.GetFrame() spec.modify(func(spec *Spec) { + helper(spec.testingTB).Helper() + if spec.immutable { spec.testingTB.Fatal(hookWarning) } diff --git a/let.go b/let.go index dfe711a..3ce994a 100644 --- a/let.go +++ b/let.go @@ -37,7 +37,7 @@ import ( // when used sparingly in any given example group, // but that can quickly degrade with heavy overuse. func Let[V any](spec *Spec, blk VarInit[V]) Var[V] { - h(spec.testingTB).Helper() + helper(spec.testingTB).Helper() return let[V](spec, makeVarID(spec), blk) } @@ -48,7 +48,7 @@ type tuple2[V, B any] struct { // Let2 is a tuple-style variable creation method, where an init block is shared between different variables. func Let2[V, B any](spec *Spec, blk func(*T) (V, B)) (Var[V], Var[B]) { - h(spec.testingTB).Helper() + helper(spec.testingTB).Helper() src := Let[tuple2[V, B]](spec, func(t *T) tuple2[V, B] { v, b := blk(t) return tuple2[V, B]{V: v, B: b} @@ -68,7 +68,7 @@ type tuple3[V, B, N any] struct { // Let3 is a tuple-style variable creation method, where an init block is shared between different variables. func Let3[V, B, N any](spec *Spec, blk func(*T) (V, B, N)) (Var[V], Var[B], Var[N]) { - h(spec.testingTB).Helper() + helper(spec.testingTB).Helper() src := Let[tuple3[V, B, N]](spec, func(t *T) tuple3[V, B, N] { v, b, n := blk(t) return tuple3[V, B, N]{V: v, B: b, N: n} @@ -89,12 +89,12 @@ please use the #Let memorization helper for now` // LetValue is a shorthand for defining immutable vars with Let under the hood. // So the function blocks can be skipped, which makes tests more readable. func LetValue[V any](spec *Spec, value V) Var[V] { - h(spec.testingTB).Helper() + helper(spec.testingTB).Helper() return letValue[V](spec, makeVarID(spec), value) } func let[V any](spec *Spec, varID string, blk VarInit[V]) Var[V] { - h(spec.testingTB).Helper() + helper(spec.testingTB).Helper() if spec.immutable { spec.testingTB.Fatalf(warnEventOnImmutableFormat, `Let`) } @@ -109,7 +109,7 @@ func let[V any](spec *Spec, varID string, blk VarInit[V]) Var[V] { } func letValue[V any](spec *Spec, varName string, value V) Var[V] { - h(spec.testingTB).Helper() + helper(spec.testingTB).Helper() if reflects.IsMutable(value) { spec.testingTB.Fatalf(panicMessageForLetValue, value) } @@ -132,7 +132,7 @@ func findCurrentDeclsFor(spec *Spec, varName string) []variablesInitBlock { } func makeVarID(spec *Spec) string { - h(spec.testingTB).Helper() + helper(spec.testingTB).Helper() location := caller.GetLocation(false) // when variable is declared within a loop // providing a variable ID offset is required to identify the variable uniquely. diff --git a/seed.go b/seed.go index 2f5ee88..1344733 100644 --- a/seed.go +++ b/seed.go @@ -29,7 +29,7 @@ func makeSeed() (int64, error) { } func seedForSpec(tb testing.TB) (_seed int64) { - h(tb).Helper() + helper(tb).Helper() if isValidTestingTB(tb) { tb.Cleanup(func() { tb.Helper()