Skip to content

Commit

Permalink
Add GinkgoTB() function (#1333)
Browse files Browse the repository at this point in the history
This function returns a wrapper around GinkgoT() which satisfies the `testing.TB` interface.

- Update fail tests with tests for GinkgoT() and GinkgoTB().
- Fix a bug where GinkgoT().Fail() was not reporting the correct line on error.
  • Loading branch information
ebabani authored Jan 9, 2024
1 parent beb9507 commit 92b6744
Show file tree
Hide file tree
Showing 12 changed files with 214 additions and 20 deletions.
2 changes: 2 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -5478,6 +5478,8 @@ When using Gomock you may want to run `ginkgo` with the `-trace` flag to print o
`GinkgoT()` also provides additional methods that are Ginkgo-specific. This allows rich third-party integrations to be built on top of Ginkgo - with GinkgoT() serving as a single connection point.
Similarly for third party libraries which accept a `testing.TB` interface, use the `GinkgoTB()` function. This function returns a struct wrapper around `GinkgoT()` which satisfies the `testing.TB`interface. If you need to use any Ginkgo-specific methods you can access the wrapped `GinkgoT()` instance using `GinkgoTBWrapper.GinkgoT`.
### IDE Support
Ginkgo works best from the command-line, and [`ginkgo watch`](#watching-for-changes) makes it easy to rerun tests on the command line whenever changes are detected.
Expand Down
2 changes: 2 additions & 0 deletions dsl/core/core_dsl.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type GinkgoTestingT = ginkgo.GinkgoTestingT
type GinkgoTInterface = ginkgo.GinkgoTInterface
type FullGinkgoTInterface = ginkgo.FullGinkgoTInterface
type SpecContext = ginkgo.SpecContext
type GinkgoTBWrapper = ginkgo.GinkgoTBWrapper

var GinkgoWriter = ginkgo.GinkgoWriter
var GinkgoLogr = ginkgo.GinkgoLogr
Expand Down Expand Up @@ -63,4 +64,5 @@ var BeforeAll = ginkgo.BeforeAll
var AfterAll = ginkgo.AfterAll
var DeferCleanup = ginkgo.DeferCleanup
var GinkgoT = ginkgo.GinkgoT
var GinkgoTB = ginkgo.GinkgoTB
var AttachProgressReporter = ginkgo.AttachProgressReporter
4 changes: 3 additions & 1 deletion dsl/dsl_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ func ExtractSymbols(f *ast.File) []string {
names := []string{}
switch v := decl.(type) {
case *ast.FuncDecl:
names = append(names, v.Name.Name)
if v.Recv == nil {
names = append(names, v.Name.Name)
}
case *ast.GenDecl:
switch v.Tok {
case token.TYPE:
Expand Down
106 changes: 94 additions & 12 deletions ginkgo_t_dsl.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package ginkgo

import (
"testing"

"github.com/onsi/ginkgo/v2/internal/testingtproxy"
"github.com/onsi/ginkgo/v2/types"
)

/*
Expand All @@ -15,7 +18,7 @@ correct line number associated with the failure - though you do not need to use
You can learn more here: https://onsi.github.io/ginkgo/#using-third-party-libraries
*/
func GinkgoT(optionalOffset ...int) FullGinkgoTInterface {
offset := 3
offset := 1
if len(optionalOffset) > 0 {
offset = optionalOffset[0]
}
Expand All @@ -41,21 +44,21 @@ The portion of the interface returned by GinkgoT() that maps onto methods in the
type GinkgoTInterface interface {
Cleanup(func())
Setenv(kev, value string)
Error(args ...interface{})
Errorf(format string, args ...interface{})
Error(args ...any)
Errorf(format string, args ...any)
Fail()
FailNow()
Failed() bool
Fatal(args ...interface{})
Fatalf(format string, args ...interface{})
Fatal(args ...any)
Fatalf(format string, args ...any)
Helper()
Log(args ...interface{})
Logf(format string, args ...interface{})
Log(args ...any)
Logf(format string, args ...any)
Name() string
Parallel()
Skip(args ...interface{})
Skip(args ...any)
SkipNow()
Skipf(format string, args ...interface{})
Skipf(format string, args ...any)
Skipped() bool
TempDir() string
}
Expand All @@ -71,9 +74,9 @@ type FullGinkgoTInterface interface {
AddReportEntryVisibilityNever(name string, args ...any)

//Prints to the GinkgoWriter
Print(a ...interface{})
Printf(format string, a ...interface{})
Println(a ...interface{})
Print(a ...any)
Printf(format string, a ...any)
Println(a ...any)

//Provides access to Ginkgo's color formatting, correctly configured to match the color settings specified in the invocation of ginkgo
F(format string, args ...any) string
Expand All @@ -92,3 +95,82 @@ type FullGinkgoTInterface interface {

AttachProgressReporter(func() string) func()
}

/*
GinkgoTB() implements a wrapper that exactly matches the testing.TB interface.
In go 1.18 a new private() function was added to the testing.TB interface. Any function which accepts testing.TB as input needs to be passed in something that directly implements testing.TB.
This wrapper satisfies the testing.TB interface and intended to be used as a drop-in replacement with third party libraries that accept testing.TB.
Similar to GinkgoT(), GinkgoTB() takes an optional offset argument that can be used to get the
correct line number associated with the failure - though you do not need to use this if you call GinkgoHelper() or GinkgoT().Helper() appropriately
*/

func GinkgoTB(optionalOffset ...int) *GinkgoTBWrapper {
offset := 2
if len(optionalOffset) > 0 {
offset = optionalOffset[0]
}
return &GinkgoTBWrapper{GinkgoT: GinkgoT(offset)}
}

type GinkgoTBWrapper struct {
testing.TB
GinkgoT FullGinkgoTInterface
}

func (g *GinkgoTBWrapper) Cleanup(f func()) {
g.GinkgoT.Cleanup(f)
}
func (g *GinkgoTBWrapper) Error(args ...any) {
g.GinkgoT.Error(args...)
}
func (g *GinkgoTBWrapper) Errorf(format string, args ...any) {
g.GinkgoT.Errorf(format, args...)
}
func (g *GinkgoTBWrapper) Fail() {
g.GinkgoT.Fail()
}
func (g *GinkgoTBWrapper) FailNow() {
g.GinkgoT.FailNow()
}
func (g *GinkgoTBWrapper) Failed() bool {
return g.GinkgoT.Failed()
}
func (g *GinkgoTBWrapper) Fatal(args ...any) {
g.GinkgoT.Fatal(args...)
}
func (g *GinkgoTBWrapper) Fatalf(format string, args ...any) {
g.GinkgoT.Fatalf(format, args...)
}
func (g *GinkgoTBWrapper) Helper() {
types.MarkAsHelper(1)
}
func (g *GinkgoTBWrapper) Log(args ...any) {
g.GinkgoT.Log(args...)
}
func (g *GinkgoTBWrapper) Logf(format string, args ...any) {
g.GinkgoT.Logf(format, args...)
}
func (g *GinkgoTBWrapper) Name() string {
return g.GinkgoT.Name()
}
func (g *GinkgoTBWrapper) Setenv(key, value string) {
g.GinkgoT.Setenv(key, value)
}
func (g *GinkgoTBWrapper) Skip(args ...any) {
g.GinkgoT.Skip(args...)
}
func (g *GinkgoTBWrapper) SkipNow() {
g.GinkgoT.SkipNow()
}
func (g *GinkgoTBWrapper) Skipf(format string, args ...any) {
g.GinkgoT.Skipf(format, args...)
}
func (g *GinkgoTBWrapper) Skipped() bool {
return g.GinkgoT.Skipped()
}
func (g *GinkgoTBWrapper) TempDir() string {
return g.GinkgoT.TempDir()
}
35 changes: 35 additions & 0 deletions integration/_fixtures/fail_fixture/fail_fixture_ginkgo_t_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package fail_fixture_test

import (
. "github.com/onsi/ginkgo/v2"
)

var _ = Describe("GinkgoT", func() {
It("synchronous failures with GinkgoT().Fail", func() {
GinkgoT().Fail()
println("NEVER SEE THIS")
})

DescribeTable("DescribeTable",
func() {
GinkgoT().Fail()
},
Entry("a TableEntry constructed by Entry"),
)

It("tracks line numbers correctly when GinkgoT().Helper() is called", func() {
ginkgoTHelper()
})

It("tracks the actual line number when no helper is used", func() {
ginkgoTNoHelper()
})
})

var ginkgoTNoHelper = func() {
GinkgoT().Fail()
}
var ginkgoTHelper = func() {
GinkgoT().Helper()
GinkgoT().Fail()
}
36 changes: 36 additions & 0 deletions integration/_fixtures/fail_fixture/fail_fixture_ginkgo_tb_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package fail_fixture_test

import (
. "github.com/onsi/ginkgo/v2"
)

var _ = Describe("GinkgoTB", func() {
It("synchronous failures with GinkgoTB().Fail", func() {
GinkgoTB().Fail()
println("NEVER SEE THIS")
})

DescribeTable("DescribeTable",
func() {
GinkgoTB().Fail()
},
Entry("a TableEntry constructed by Entry"),
)

It("tracks line numbers correctly when GinkgoTB().Helper() is called", func() {
ginkgoTBHelper()
})

It("tracks the actual line number when no GinkgoTB helper is used", func() {
ginkgoTBNoHelper()
})
})

var ginkgoTBNoHelper = func() {
GinkgoTB().Fail()
}
var ginkgoTBHelper = func() {
t := GinkgoTB()
t.Helper()
t.Fail()
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package more_ginkgo_tests_test

import (
"testing"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/ginkgo/v2/integration/_fixtures/more_ginkgo_tests_fixture"
. "github.com/onsi/gomega"
Expand All @@ -14,4 +16,12 @@ var _ = Describe("MoreGinkgoTests", func() {
It("should always pass", func() {
Ω(AlwaysTrue()).Should(BeTrue())
})

It("should match testing.TB", func() {
var tbFunc = func(_ testing.TB) {
Ω(AlwaysTrue()).Should(BeTrue())
}

tbFunc(GinkgoTB())
})
})
26 changes: 25 additions & 1 deletion integration/fail_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,31 @@ var _ = Describe("Failing Specs", func() {
Ω(output).Should(ContainSubstring(`a helper failed`))
Ω(output).Should(ContainSubstring(`fail_fixture_test.go:54`), "the code location reported for the helper failure - we're testing the call to GinkgoHelper() works as expected")

Ω(output).Should(ContainSubstring("0 Passed | 8 Failed"))
Ω(output).Should(ContainSubstring("synchronous failures with GinkgoT().Fail"))
Ω(output).Should(ContainSubstring("fail_fixture_ginkgo_t_test.go:9"))

Ω(output).Should(ContainSubstring("GinkgoT DescribeTable"))
Ω(output).Should(ContainSubstring("fail_fixture_ginkgo_t_test.go:15"))

Ω(output).Should(ContainSubstring(`tracks line numbers correctly when GinkgoT().Helper() is called`))
Ω(output).Should(ContainSubstring(`fail_fixture_ginkgo_t_test.go:21`), "the code location reported for the ginkgoT helper failure")

Ω(output).Should(ContainSubstring(`tracks the actual line number when no helper is used`))
Ω(output).Should(ContainSubstring(`fail_fixture_ginkgo_t_test.go:30`), "the code location reported for the ginkgoT no helper failure")

Ω(output).Should(ContainSubstring("synchronous failures with GinkgoTB().Fail"))
Ω(output).Should(ContainSubstring("fail_fixture_ginkgo_tb_test.go:9"))

Ω(output).Should(ContainSubstring("GinkgoTB DescribeTable"))
Ω(output).Should(ContainSubstring("fail_fixture_ginkgo_tb_test.go:15"))

Ω(output).Should(ContainSubstring(`tracks line numbers correctly when GinkgoTB().Helper() is called`))
Ω(output).Should(ContainSubstring(`fail_fixture_ginkgo_tb_test.go:21`), "the code location reported for the ginkgoTB helper failure")

Ω(output).Should(ContainSubstring(`tracks the actual line number when no GinkgoTB helper is used`))
Ω(output).Should(ContainSubstring(`fail_fixture_ginkgo_tb_test.go:30`), "the code location reported for the ginkgoT no helper failure")

Ω(output).Should(ContainSubstring("0 Passed | 16 Failed"))
})
})

Expand Down
4 changes: 2 additions & 2 deletions integration/flags_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,8 @@ var _ = Describe("Flags Specs", func() {
output := string(session.Out.Contents())

Ω(output).Should(ContainSubstring("synchronous failures"))
Ω(output).Should(ContainSubstring("8 Specs"))
Ω(output).Should(ContainSubstring("8 Passed"))
Ω(output).Should(ContainSubstring("16 Specs"))
Ω(output).Should(ContainSubstring("16 Passed"))
Ω(output).Should(ContainSubstring("0 Failed"))
})

Expand Down
6 changes: 3 additions & 3 deletions integration/run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ var _ = Describe("Running Specs", func() {
output := string(session.Out.Contents())

outputLines := strings.Split(output, "\n")
Ω(outputLines[0]).Should(MatchRegexp(`\[\d+\] More_ginkgo_tests Suite - 2/2 specs [%s]{2} SUCCESS! \d+(\.\d+)?[muµ]s PASS`, regexp.QuoteMeta(denoter)))
Ω(outputLines[0]).Should(MatchRegexp(`\[\d+\] More_ginkgo_tests Suite - 3/3 specs [%s]{3} SUCCESS! \d+(\.\d+)?[muµ]s PASS`, regexp.QuoteMeta(denoter)))
Ω(outputLines[1]).Should(ContainSubstring("Skipping ./no_tagged_tests (no test files)"))
Ω(outputLines[2]).Should(MatchRegexp(`\[\d+\] Passing_ginkgo_tests Suite - 5/5 specs [%s]{5} SUCCESS! \d+(\.\d+)?[muµ]s PASS`, regexp.QuoteMeta(denoter)))
Ω(output).Should(ContainSubstring("Test Suite Passed"))
Expand All @@ -352,7 +352,7 @@ var _ = Describe("Running Specs", func() {
output := string(session.Out.Contents())

outputLines := strings.Split(output, "\n")
Ω(outputLines[0]).Should(MatchRegexp(`\[\d+\] More_ginkgo_tests Suite - 2/2 specs [%s]{2} SUCCESS! \d+(\.\d+)?[muµ]s PASS`, regexp.QuoteMeta(denoter)))
Ω(outputLines[0]).Should(MatchRegexp(`\[\d+\] More_ginkgo_tests Suite - 3/3 specs [%s]{3} SUCCESS! \d+(\.\d+)?[muµ]s PASS`, regexp.QuoteMeta(denoter)))
Ω(outputLines[1]).Should(ContainSubstring("Skipping ./no_tagged_tests (no test files)"))
Ω(outputLines[2]).Should(MatchRegexp(`\[\d+\] Passing_ginkgo_tests Suite - 5/5 specs [%s]{5} SUCCESS! \d+(\.\d+)?[muµ]s PASS`, regexp.QuoteMeta(denoter)))
Ω(output).Should(ContainSubstring("Test Suite Passed"))
Expand Down Expand Up @@ -416,7 +416,7 @@ var _ = Describe("Running Specs", func() {
Ω(outputLines[2]).Should(ContainSubstring("Failed to compile does_not_compile:"))
Ω(output).Should(MatchRegexp(`\[\d+\] Failing_ginkgo_tests Suite - 2/2 specs`))
Ω(output).Should(ContainSubstring(fmt.Sprintf("%s [FAILED]", denoter)))
Ω(output).Should(MatchRegexp(`\[\d+\] More_ginkgo_tests Suite - 2/2 specs [%s]{2} SUCCESS! \d+(\.\d+)?[muµ]s PASS`, regexp.QuoteMeta(denoter)))
Ω(output).Should(MatchRegexp(`\[\d+\] More_ginkgo_tests Suite - 3/3 specs [%s]{3} SUCCESS! \d+(\.\d+)?[muµ]s PASS`, regexp.QuoteMeta(denoter)))
Ω(output).Should(ContainSubstring("Test Suite Failed"))
})
})
Expand Down
2 changes: 1 addition & 1 deletion integration/verbose_and_succinct_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ var _ = Describe("Verbose And Succinct Mode", func() {
output := session.Out.Contents()

Ω(output).Should(MatchRegexp(`\] Passing_ginkgo_tests Suite - 5/5 specs [%s]{5} SUCCESS!`, regexp.QuoteMeta(denoter)))
Ω(output).Should(MatchRegexp(`\] More_ginkgo_tests Suite - 2/2 specs [%s]{2} SUCCESS!`, regexp.QuoteMeta(denoter)))
Ω(output).Should(MatchRegexp(`\] More_ginkgo_tests Suite - 3/3 specs [%s]{3} SUCCESS!`, regexp.QuoteMeta(denoter)))
})
})

Expand Down
1 change: 1 addition & 0 deletions internal/testingtproxy/testingtproxy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ var _ = Describe("Testingtproxy", func() {
reportToReturn.LeafNodeText = "Lewis"
Ω(t.Name()).Should(Equal("C.S. Lewis"))
Ω(GinkgoT().Name()).Should(ContainSubstring("supports Name"))
Ω(GinkgoTB().Name()).Should(ContainSubstring("supports Name"))
})

It("ignores Parallel", func() {
Expand Down

0 comments on commit 92b6744

Please sign in to comment.