123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610 |
- /*
- Ginkgo is a BDD-style testing framework for Golang
- The godoc documentation describes Ginkgo's API. More comprehensive documentation (with examples!) is available at http://onsi.github.io/ginkgo/
- Ginkgo's preferred matcher library is [Gomega](http://github.com/onsi/gomega)
- Ginkgo on Github: http://github.com/onsi/ginkgo
- Ginkgo is MIT-Licensed
- */
- package ginkgo
- import (
- "flag"
- "fmt"
- "io"
- "net/http"
- "os"
- "strings"
- "time"
- "github.com/onsi/ginkgo/config"
- "github.com/onsi/ginkgo/internal/codelocation"
- "github.com/onsi/ginkgo/internal/failer"
- "github.com/onsi/ginkgo/internal/remote"
- "github.com/onsi/ginkgo/internal/suite"
- "github.com/onsi/ginkgo/internal/testingtproxy"
- "github.com/onsi/ginkgo/internal/writer"
- "github.com/onsi/ginkgo/reporters"
- "github.com/onsi/ginkgo/reporters/stenographer"
- colorable "github.com/onsi/ginkgo/reporters/stenographer/support/go-colorable"
- "github.com/onsi/ginkgo/types"
- )
- const GINKGO_VERSION = config.VERSION
- const GINKGO_PANIC = `
- Your test failed.
- Ginkgo panics to prevent subsequent assertions from running.
- Normally Ginkgo rescues this panic so you shouldn't see it.
- But, if you make an assertion in a goroutine, Ginkgo can't capture the panic.
- To circumvent this, you should call
- defer GinkgoRecover()
- at the top of the goroutine that caused this panic.
- `
- const defaultTimeout = 1
- var globalSuite *suite.Suite
- var globalFailer *failer.Failer
- func init() {
- config.Flags(flag.CommandLine, "ginkgo", true)
- GinkgoWriter = writer.New(os.Stdout)
- globalFailer = failer.New()
- globalSuite = suite.New(globalFailer)
- }
- //GinkgoWriter implements an io.Writer
- //When running in verbose mode any writes to GinkgoWriter will be immediately printed
- //to stdout. Otherwise, GinkgoWriter will buffer any writes produced during the current test and flush them to screen
- //only if the current test fails.
- var GinkgoWriter io.Writer
- //The interface by which Ginkgo receives *testing.T
- type GinkgoTestingT interface {
- Fail()
- }
- //GinkgoRandomSeed returns the seed used to randomize spec execution order. It is
- //useful for seeding your own pseudorandom number generators (PRNGs) to ensure
- //consistent executions from run to run, where your tests contain variability (for
- //example, when selecting random test data).
- func GinkgoRandomSeed() int64 {
- return config.GinkgoConfig.RandomSeed
- }
- //GinkgoParallelNode returns the parallel node number for the current ginkgo process
- //The node number is 1-indexed
- func GinkgoParallelNode() int {
- return config.GinkgoConfig.ParallelNode
- }
- //Some matcher libraries or legacy codebases require a *testing.T
- //GinkgoT implements an interface analogous to *testing.T and can be used if
- //the library in question accepts *testing.T through an interface
- //
- // For example, with testify:
- // assert.Equal(GinkgoT(), 123, 123, "they should be equal")
- //
- // Or with gomock:
- // gomock.NewController(GinkgoT())
- //
- // GinkgoT() takes an optional offset argument that can be used to get the
- // correct line number associated with the failure.
- func GinkgoT(optionalOffset ...int) GinkgoTInterface {
- offset := 3
- if len(optionalOffset) > 0 {
- offset = optionalOffset[0]
- }
- return testingtproxy.New(GinkgoWriter, Fail, offset)
- }
- //The interface returned by GinkgoT(). This covers most of the methods
- //in the testing package's T.
- type GinkgoTInterface interface {
- Fail()
- Error(args ...interface{})
- Errorf(format string, args ...interface{})
- FailNow()
- Fatal(args ...interface{})
- Fatalf(format string, args ...interface{})
- Log(args ...interface{})
- Logf(format string, args ...interface{})
- Failed() bool
- Parallel()
- Skip(args ...interface{})
- Skipf(format string, args ...interface{})
- SkipNow()
- Skipped() bool
- }
- //Custom Ginkgo test reporters must implement the Reporter interface.
- //
- //The custom reporter is passed in a SuiteSummary when the suite begins and ends,
- //and a SpecSummary just before a spec begins and just after a spec ends
- type Reporter reporters.Reporter
- //Asynchronous specs are given a channel of the Done type. You must close or write to the channel
- //to tell Ginkgo that your async test is done.
- type Done chan<- interface{}
- //GinkgoTestDescription represents the information about the current running test returned by CurrentGinkgoTestDescription
- // FullTestText: a concatenation of ComponentTexts and the TestText
- // ComponentTexts: a list of all texts for the Describes & Contexts leading up to the current test
- // TestText: the text in the actual It or Measure node
- // IsMeasurement: true if the current test is a measurement
- // FileName: the name of the file containing the current test
- // LineNumber: the line number for the current test
- // Failed: if the current test has failed, this will be true (useful in an AfterEach)
- type GinkgoTestDescription struct {
- FullTestText string
- ComponentTexts []string
- TestText string
- IsMeasurement bool
- FileName string
- LineNumber int
- Failed bool
- Duration time.Duration
- }
- //CurrentGinkgoTestDescripton returns information about the current running test.
- func CurrentGinkgoTestDescription() GinkgoTestDescription {
- summary, ok := globalSuite.CurrentRunningSpecSummary()
- if !ok {
- return GinkgoTestDescription{}
- }
- subjectCodeLocation := summary.ComponentCodeLocations[len(summary.ComponentCodeLocations)-1]
- return GinkgoTestDescription{
- ComponentTexts: summary.ComponentTexts[1:],
- FullTestText: strings.Join(summary.ComponentTexts[1:], " "),
- TestText: summary.ComponentTexts[len(summary.ComponentTexts)-1],
- IsMeasurement: summary.IsMeasurement,
- FileName: subjectCodeLocation.FileName,
- LineNumber: subjectCodeLocation.LineNumber,
- Failed: summary.HasFailureState(),
- Duration: summary.RunTime,
- }
- }
- //Measurement tests receive a Benchmarker.
- //
- //You use the Time() function to time how long the passed in body function takes to run
- //You use the RecordValue() function to track arbitrary numerical measurements.
- //The RecordValueWithPrecision() function can be used alternatively to provide the unit
- //and resolution of the numeric measurement.
- //The optional info argument is passed to the test reporter and can be used to
- // provide the measurement data to a custom reporter with context.
- //
- //See http://onsi.github.io/ginkgo/#benchmark_tests for more details
- type Benchmarker interface {
- Time(name string, body func(), info ...interface{}) (elapsedTime time.Duration)
- RecordValue(name string, value float64, info ...interface{})
- RecordValueWithPrecision(name string, value float64, units string, precision int, info ...interface{})
- }
- //RunSpecs is the entry point for the Ginkgo test runner.
- //You must call this within a Golang testing TestX(t *testing.T) function.
- //
- //To bootstrap a test suite you can use the Ginkgo CLI:
- //
- // ginkgo bootstrap
- func RunSpecs(t GinkgoTestingT, description string) bool {
- specReporters := []Reporter{buildDefaultReporter()}
- return RunSpecsWithCustomReporters(t, description, specReporters)
- }
- //To run your tests with Ginkgo's default reporter and your custom reporter(s), replace
- //RunSpecs() with this method.
- func RunSpecsWithDefaultAndCustomReporters(t GinkgoTestingT, description string, specReporters []Reporter) bool {
- specReporters = append(specReporters, buildDefaultReporter())
- return RunSpecsWithCustomReporters(t, description, specReporters)
- }
- //To run your tests with your custom reporter(s) (and *not* Ginkgo's default reporter), replace
- //RunSpecs() with this method. Note that parallel tests will not work correctly without the default reporter
- func RunSpecsWithCustomReporters(t GinkgoTestingT, description string, specReporters []Reporter) bool {
- writer := GinkgoWriter.(*writer.Writer)
- writer.SetStream(config.DefaultReporterConfig.Verbose)
- reporters := make([]reporters.Reporter, len(specReporters))
- for i, reporter := range specReporters {
- reporters[i] = reporter
- }
- passed, hasFocusedTests := globalSuite.Run(t, description, reporters, writer, config.GinkgoConfig)
- if passed && hasFocusedTests && strings.TrimSpace(os.Getenv("GINKGO_EDITOR_INTEGRATION")) == "" {
- fmt.Println("PASS | FOCUSED")
- os.Exit(types.GINKGO_FOCUS_EXIT_CODE)
- }
- return passed
- }
- func buildDefaultReporter() Reporter {
- remoteReportingServer := config.GinkgoConfig.StreamHost
- if remoteReportingServer == "" {
- stenographer := stenographer.New(!config.DefaultReporterConfig.NoColor, config.GinkgoConfig.FlakeAttempts > 1, colorable.NewColorableStdout())
- return reporters.NewDefaultReporter(config.DefaultReporterConfig, stenographer)
- } else {
- debugFile := ""
- if config.GinkgoConfig.DebugParallel {
- debugFile = fmt.Sprintf("ginkgo-node-%d.log", config.GinkgoConfig.ParallelNode)
- }
- return remote.NewForwardingReporter(config.DefaultReporterConfig, remoteReportingServer, &http.Client{}, remote.NewOutputInterceptor(), GinkgoWriter.(*writer.Writer), debugFile)
- }
- }
- //Skip notifies Ginkgo that the current spec was skipped.
- func Skip(message string, callerSkip ...int) {
- skip := 0
- if len(callerSkip) > 0 {
- skip = callerSkip[0]
- }
- globalFailer.Skip(message, codelocation.New(skip+1))
- panic(GINKGO_PANIC)
- }
- //Fail notifies Ginkgo that the current spec has failed. (Gomega will call Fail for you automatically when an assertion fails.)
- func Fail(message string, callerSkip ...int) {
- skip := 0
- if len(callerSkip) > 0 {
- skip = callerSkip[0]
- }
- globalFailer.Fail(message, codelocation.New(skip+1))
- panic(GINKGO_PANIC)
- }
- //GinkgoRecover should be deferred at the top of any spawned goroutine that (may) call `Fail`
- //Since Gomega assertions call fail, you should throw a `defer GinkgoRecover()` at the top of any goroutine that
- //calls out to Gomega
- //
- //Here's why: Ginkgo's `Fail` method records the failure and then panics to prevent
- //further assertions from running. This panic must be recovered. Ginkgo does this for you
- //if the panic originates in a Ginkgo node (an It, BeforeEach, etc...)
- //
- //Unfortunately, if a panic originates on a goroutine *launched* from one of these nodes there's no
- //way for Ginkgo to rescue the panic. To do this, you must remember to `defer GinkgoRecover()` at the top of such a goroutine.
- func GinkgoRecover() {
- e := recover()
- if e != nil {
- globalFailer.Panic(codelocation.New(1), e)
- }
- }
- //Describe blocks allow you to organize your specs. A Describe block can contain any number of
- //BeforeEach, AfterEach, JustBeforeEach, It, and Measurement blocks.
- //
- //In addition you can nest Describe, Context and When blocks. Describe, Context and When blocks are functionally
- //equivalent. The difference is purely semantic -- you typical Describe the behavior of an object
- //or method and, within that Describe, outline a number of Contexts and Whens.
- func Describe(text string, body func()) bool {
- globalSuite.PushContainerNode(text, body, types.FlagTypeNone, codelocation.New(1))
- return true
- }
- //You can focus the tests within a describe block using FDescribe
- func FDescribe(text string, body func()) bool {
- globalSuite.PushContainerNode(text, body, types.FlagTypeFocused, codelocation.New(1))
- return true
- }
- //You can mark the tests within a describe block as pending using PDescribe
- func PDescribe(text string, body func()) bool {
- globalSuite.PushContainerNode(text, body, types.FlagTypePending, codelocation.New(1))
- return true
- }
- //You can mark the tests within a describe block as pending using XDescribe
- func XDescribe(text string, body func()) bool {
- globalSuite.PushContainerNode(text, body, types.FlagTypePending, codelocation.New(1))
- return true
- }
- //Context blocks allow you to organize your specs. A Context block can contain any number of
- //BeforeEach, AfterEach, JustBeforeEach, It, and Measurement blocks.
- //
- //In addition you can nest Describe, Context and When blocks. Describe, Context and When blocks are functionally
- //equivalent. The difference is purely semantic -- you typical Describe the behavior of an object
- //or method and, within that Describe, outline a number of Contexts and Whens.
- func Context(text string, body func()) bool {
- globalSuite.PushContainerNode(text, body, types.FlagTypeNone, codelocation.New(1))
- return true
- }
- //You can focus the tests within a describe block using FContext
- func FContext(text string, body func()) bool {
- globalSuite.PushContainerNode(text, body, types.FlagTypeFocused, codelocation.New(1))
- return true
- }
- //You can mark the tests within a describe block as pending using PContext
- func PContext(text string, body func()) bool {
- globalSuite.PushContainerNode(text, body, types.FlagTypePending, codelocation.New(1))
- return true
- }
- //You can mark the tests within a describe block as pending using XContext
- func XContext(text string, body func()) bool {
- globalSuite.PushContainerNode(text, body, types.FlagTypePending, codelocation.New(1))
- return true
- }
- //When blocks allow you to organize your specs. A When block can contain any number of
- //BeforeEach, AfterEach, JustBeforeEach, It, and Measurement blocks.
- //
- //In addition you can nest Describe, Context and When blocks. Describe, Context and When blocks are functionally
- //equivalent. The difference is purely semantic -- you typical Describe the behavior of an object
- //or method and, within that Describe, outline a number of Contexts and Whens.
- func When(text string, body func()) bool {
- globalSuite.PushContainerNode("when "+text, body, types.FlagTypeNone, codelocation.New(1))
- return true
- }
- //You can focus the tests within a describe block using FWhen
- func FWhen(text string, body func()) bool {
- globalSuite.PushContainerNode("when "+text, body, types.FlagTypeFocused, codelocation.New(1))
- return true
- }
- //You can mark the tests within a describe block as pending using PWhen
- func PWhen(text string, body func()) bool {
- globalSuite.PushContainerNode("when "+text, body, types.FlagTypePending, codelocation.New(1))
- return true
- }
- //You can mark the tests within a describe block as pending using XWhen
- func XWhen(text string, body func()) bool {
- globalSuite.PushContainerNode("when "+text, body, types.FlagTypePending, codelocation.New(1))
- return true
- }
- //It blocks contain your test code and assertions. You cannot nest any other Ginkgo blocks
- //within an It block.
- //
- //Ginkgo will normally run It blocks synchronously. To perform asynchronous tests, pass a
- //function that accepts a Done channel. When you do this, you can also provide an optional timeout.
- func It(text string, body interface{}, timeout ...float64) bool {
- globalSuite.PushItNode(text, body, types.FlagTypeNone, codelocation.New(1), parseTimeout(timeout...))
- return true
- }
- //You can focus individual Its using FIt
- func FIt(text string, body interface{}, timeout ...float64) bool {
- globalSuite.PushItNode(text, body, types.FlagTypeFocused, codelocation.New(1), parseTimeout(timeout...))
- return true
- }
- //You can mark Its as pending using PIt
- func PIt(text string, _ ...interface{}) bool {
- globalSuite.PushItNode(text, func() {}, types.FlagTypePending, codelocation.New(1), 0)
- return true
- }
- //You can mark Its as pending using XIt
- func XIt(text string, _ ...interface{}) bool {
- globalSuite.PushItNode(text, func() {}, types.FlagTypePending, codelocation.New(1), 0)
- return true
- }
- //Specify blocks are aliases for It blocks and allow for more natural wording in situations
- //which "It" does not fit into a natural sentence flow. All the same protocols apply for Specify blocks
- //which apply to It blocks.
- func Specify(text string, body interface{}, timeout ...float64) bool {
- globalSuite.PushItNode(text, body, types.FlagTypeNone, codelocation.New(1), parseTimeout(timeout...))
- return true
- }
- //You can focus individual Specifys using FSpecify
- func FSpecify(text string, body interface{}, timeout ...float64) bool {
- globalSuite.PushItNode(text, body, types.FlagTypeFocused, codelocation.New(1), parseTimeout(timeout...))
- return true
- }
- //You can mark Specifys as pending using PSpecify
- func PSpecify(text string, is ...interface{}) bool {
- globalSuite.PushItNode(text, func() {}, types.FlagTypePending, codelocation.New(1), 0)
- return true
- }
- //You can mark Specifys as pending using XSpecify
- func XSpecify(text string, is ...interface{}) bool {
- globalSuite.PushItNode(text, func() {}, types.FlagTypePending, codelocation.New(1), 0)
- return true
- }
- //By allows you to better document large Its.
- //
- //Generally you should try to keep your Its short and to the point. This is not always possible, however,
- //especially in the context of integration tests that capture a particular workflow.
- //
- //By allows you to document such flows. By must be called within a runnable node (It, BeforeEach, Measure, etc...)
- //By will simply log the passed in text to the GinkgoWriter. If By is handed a function it will immediately run the function.
- func By(text string, callbacks ...func()) {
- preamble := "\x1b[1mSTEP\x1b[0m"
- if config.DefaultReporterConfig.NoColor {
- preamble = "STEP"
- }
- fmt.Fprintln(GinkgoWriter, preamble+": "+text)
- if len(callbacks) == 1 {
- callbacks[0]()
- }
- if len(callbacks) > 1 {
- panic("just one callback per By, please")
- }
- }
- //Measure blocks run the passed in body function repeatedly (determined by the samples argument)
- //and accumulate metrics provided to the Benchmarker by the body function.
- //
- //The body function must have the signature:
- // func(b Benchmarker)
- func Measure(text string, body interface{}, samples int) bool {
- globalSuite.PushMeasureNode(text, body, types.FlagTypeNone, codelocation.New(1), samples)
- return true
- }
- //You can focus individual Measures using FMeasure
- func FMeasure(text string, body interface{}, samples int) bool {
- globalSuite.PushMeasureNode(text, body, types.FlagTypeFocused, codelocation.New(1), samples)
- return true
- }
- //You can mark Maeasurements as pending using PMeasure
- func PMeasure(text string, _ ...interface{}) bool {
- globalSuite.PushMeasureNode(text, func(b Benchmarker) {}, types.FlagTypePending, codelocation.New(1), 0)
- return true
- }
- //You can mark Maeasurements as pending using XMeasure
- func XMeasure(text string, _ ...interface{}) bool {
- globalSuite.PushMeasureNode(text, func(b Benchmarker) {}, types.FlagTypePending, codelocation.New(1), 0)
- return true
- }
- //BeforeSuite blocks are run just once before any specs are run. When running in parallel, each
- //parallel node process will call BeforeSuite.
- //
- //BeforeSuite blocks can be made asynchronous by providing a body function that accepts a Done channel
- //
- //You may only register *one* BeforeSuite handler per test suite. You typically do so in your bootstrap file at the top level.
- func BeforeSuite(body interface{}, timeout ...float64) bool {
- globalSuite.SetBeforeSuiteNode(body, codelocation.New(1), parseTimeout(timeout...))
- return true
- }
- //AfterSuite blocks are *always* run after all the specs regardless of whether specs have passed or failed.
- //Moreover, if Ginkgo receives an interrupt signal (^C) it will attempt to run the AfterSuite before exiting.
- //
- //When running in parallel, each parallel node process will call AfterSuite.
- //
- //AfterSuite blocks can be made asynchronous by providing a body function that accepts a Done channel
- //
- //You may only register *one* AfterSuite handler per test suite. You typically do so in your bootstrap file at the top level.
- func AfterSuite(body interface{}, timeout ...float64) bool {
- globalSuite.SetAfterSuiteNode(body, codelocation.New(1), parseTimeout(timeout...))
- return true
- }
- //SynchronizedBeforeSuite blocks are primarily meant to solve the problem of setting up singleton external resources shared across
- //nodes when running tests in parallel. For example, say you have a shared database that you can only start one instance of that
- //must be used in your tests. When running in parallel, only one node should set up the database and all other nodes should wait
- //until that node is done before running.
- //
- //SynchronizedBeforeSuite accomplishes this by taking *two* function arguments. The first is only run on parallel node #1. The second is
- //run on all nodes, but *only* after the first function completes succesfully. Ginkgo also makes it possible to send data from the first function (on Node 1)
- //to the second function (on all the other nodes).
- //
- //The functions have the following signatures. The first function (which only runs on node 1) has the signature:
- //
- // func() []byte
- //
- //or, to run asynchronously:
- //
- // func(done Done) []byte
- //
- //The byte array returned by the first function is then passed to the second function, which has the signature:
- //
- // func(data []byte)
- //
- //or, to run asynchronously:
- //
- // func(data []byte, done Done)
- //
- //Here's a simple pseudo-code example that starts a shared database on Node 1 and shares the database's address with the other nodes:
- //
- // var dbClient db.Client
- // var dbRunner db.Runner
- //
- // var _ = SynchronizedBeforeSuite(func() []byte {
- // dbRunner = db.NewRunner()
- // err := dbRunner.Start()
- // Ω(err).ShouldNot(HaveOccurred())
- // return []byte(dbRunner.URL)
- // }, func(data []byte) {
- // dbClient = db.NewClient()
- // err := dbClient.Connect(string(data))
- // Ω(err).ShouldNot(HaveOccurred())
- // })
- func SynchronizedBeforeSuite(node1Body interface{}, allNodesBody interface{}, timeout ...float64) bool {
- globalSuite.SetSynchronizedBeforeSuiteNode(
- node1Body,
- allNodesBody,
- codelocation.New(1),
- parseTimeout(timeout...),
- )
- return true
- }
- //SynchronizedAfterSuite blocks complement the SynchronizedBeforeSuite blocks in solving the problem of setting up
- //external singleton resources shared across nodes when running tests in parallel.
- //
- //SynchronizedAfterSuite accomplishes this by taking *two* function arguments. The first runs on all nodes. The second runs only on parallel node #1
- //and *only* after all other nodes have finished and exited. This ensures that node 1, and any resources it is running, remain alive until
- //all other nodes are finished.
- //
- //Both functions have the same signature: either func() or func(done Done) to run asynchronously.
- //
- //Here's a pseudo-code example that complements that given in SynchronizedBeforeSuite. Here, SynchronizedAfterSuite is used to tear down the shared database
- //only after all nodes have finished:
- //
- // var _ = SynchronizedAfterSuite(func() {
- // dbClient.Cleanup()
- // }, func() {
- // dbRunner.Stop()
- // })
- func SynchronizedAfterSuite(allNodesBody interface{}, node1Body interface{}, timeout ...float64) bool {
- globalSuite.SetSynchronizedAfterSuiteNode(
- allNodesBody,
- node1Body,
- codelocation.New(1),
- parseTimeout(timeout...),
- )
- return true
- }
- //BeforeEach blocks are run before It blocks. When multiple BeforeEach blocks are defined in nested
- //Describe and Context blocks the outermost BeforeEach blocks are run first.
- //
- //Like It blocks, BeforeEach blocks can be made asynchronous by providing a body function that accepts
- //a Done channel
- func BeforeEach(body interface{}, timeout ...float64) bool {
- globalSuite.PushBeforeEachNode(body, codelocation.New(1), parseTimeout(timeout...))
- return true
- }
- //JustBeforeEach blocks are run before It blocks but *after* all BeforeEach blocks. For more details,
- //read the [documentation](http://onsi.github.io/ginkgo/#separating_creation_and_configuration_)
- //
- //Like It blocks, BeforeEach blocks can be made asynchronous by providing a body function that accepts
- //a Done channel
- func JustBeforeEach(body interface{}, timeout ...float64) bool {
- globalSuite.PushJustBeforeEachNode(body, codelocation.New(1), parseTimeout(timeout...))
- return true
- }
- //AfterEach blocks are run after It blocks. When multiple AfterEach blocks are defined in nested
- //Describe and Context blocks the innermost AfterEach blocks are run first.
- //
- //Like It blocks, AfterEach blocks can be made asynchronous by providing a body function that accepts
- //a Done channel
- func AfterEach(body interface{}, timeout ...float64) bool {
- globalSuite.PushAfterEachNode(body, codelocation.New(1), parseTimeout(timeout...))
- return true
- }
- func parseTimeout(timeout ...float64) time.Duration {
- if len(timeout) == 0 {
- return time.Duration(defaultTimeout * int64(time.Second))
- } else {
- return time.Duration(timeout[0] * float64(time.Second))
- }
- }
|