Skip to content

Commit

Permalink
add support for Spec#AfterAll
Browse files Browse the repository at this point in the history
`Spec#AfterAll` is perfect for cleaning up shared states across tests (like a DB connection).
It's also useful when you need to make assertions that should only happen after all the tests have finished running.
  • Loading branch information
adamluzsi committed Sep 17, 2024
1 parent 87798da commit b915aa3
Show file tree
Hide file tree
Showing 3 changed files with 146 additions and 2 deletions.
27 changes: 27 additions & 0 deletions Spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ type Spec struct {
hooks struct {
Around []hook
BeforeAll []hookOnce
AfterAll []hookOnce
}

defs []func(*Spec)
Expand Down Expand Up @@ -602,13 +603,39 @@ func (spec *Spec) Finish() {
spec.orderer.Order(tests)
td := &teardown.Teardown{}
defer spec.documentResults()
defer spec.runAfterAll()
defer td.Finish()
for _, tc := range tests {
tc()
}
})
}

func (spec *Spec) runAfterAll() {
helper(spec.testingTB).Helper()
if spec.testingTB == nil {
return
}
if spec.parent != nil {
return
}
if spec.isSuite() {
return
}
spec.visitAll(func(s *Spec) {
for _, h := range s.hooks.AfterAll {
h.DoOnce(spec.testingTB)
}
})
}

func (spec *Spec) visitAll(fn func(*Spec)) {
fn(spec)
for _, child := range spec.children {
child.visitAll(fn)
}
}

func (spec *Spec) documentResults() {
if spec.testingTB == nil {
return
Expand Down
27 changes: 25 additions & 2 deletions hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,6 @@ func (spec *Spec) Before(beforeBlock tBlock) {
// The received *testing.T object is the same as the Then block *testing.T object
// This hook applied to this scope and anything that is nested from here.
// All setup block is stackable.
//
// DEPRECATED: use Spec.Before with T.Cleanup or Spec.Before with T.Defer instead
func (spec *Spec) After(afterBlock tBlock) {
helper(spec.testingTB).Helper()
spec.Around(func(t *T) func() {
Expand Down Expand Up @@ -99,3 +97,28 @@ func (spec *Spec) BeforeAll(blk func(tb testing.TB)) {
spec.hooks.BeforeAll = append(spec.hooks.BeforeAll, h)
})
}

func (spec *Spec) AfterAll(blk func(tb testing.TB)) {
helper(spec.testingTB).Helper()
frame, _ := caller.GetFrame()
spec.modify(func(spec *Spec) {
helper(spec.testingTB).Helper()

if spec.immutable {
spec.testingTB.Fatal(hookWarning)
}

var onCall sync.Once
var beforeAll = func(tb testing.TB) {
onCall.Do(func() { blk(tb) })
}

h := hookOnce{
DoOnce: beforeAll,
Block: blk,
Frame: frame,
}

spec.hooks.AfterAll = append(spec.hooks.AfterAll, h)
})
}
94 changes: 94 additions & 0 deletions hooks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,97 @@ func TestSpec_BeforeAll_failIfDefinedAfterTestCases(t *testing.T) {
})
assert.Must(t).True(stub.IsFailed)
}

func ExampleSpec_AfterAll() {
s := testcase.NewSpec(nil)
s.AfterAll(func(tb testing.TB) {
// do something after all the test finished running
})
s.Test("this test will run before the AfterAll hook", func(t *testcase.T) {})
}

func TestSpec_AfterAll(t *testing.T) {
stub := &doubles.TB{}
var order []string
sandbox.Run(func() {
s := testcase.NewSpec(stub)
s.HasSideEffect()
s.AfterAll(func(tb testing.TB) {
order = append(order, "AfterAll")
})
s.Test(``, func(t *testcase.T) {
order = append(order, "Test")
})
s.Test(``, func(t *testcase.T) {
order = append(order, "Test")
})
s.Finish()
})
assert.Must(t).False(stub.IsFailed)
assert.Equal(t, []string{"Test", "Test", "AfterAll"}, order,
`expected to only run once (single "AfterAll" in the order array)`,
`and it should have run in order (After all the "Test")`,
)
}

func TestSpec_AfterAll_nested(t *testing.T) {
stub := &doubles.TB{}
var order []string
sandbox.Run(func() {
s := testcase.NewSpec(stub)
s.HasSideEffect()
s.AfterAll(func(tb testing.TB) {
order = append(order, "AfterAll")
})
s.Context(``, func(s *testcase.Spec) {
s.AfterAll(func(tb testing.TB) {
order = append(order, "AfterAll")
})
s.Test(``, func(t *testcase.T) {
order = append(order, "Test")
})
})
s.Test(``, func(t *testcase.T) {
order = append(order, "Test")
})
s.Finish()
})
assert.Must(t).False(stub.IsFailed)
assert.Equal(t, []string{"Test", "Test", "AfterAll", "AfterAll"}, order)
}

func TestSpec_AfterAll_suite(t *testing.T) {
stub := &doubles.TB{}
var order []string
sandbox.Run(func() {
suiteSpec1 := testcase.NewSpec(nil)
suiteSpec1.HasSideEffect()
suiteSpec1.AfterAll(func(tb testing.TB) {
order = append(order, "AfterAll")
})
suiteSpec1.Test(``, func(t *testcase.T) {
order = append(order, "Test")
})
suite1 := suiteSpec1.AsSuite("suite")
suiteSpec2 := testcase.NewSpec(nil)
suiteSpec2.Context("", suite1.Spec)

ss := testcase.NewSpec(stub)
ss.Context("", suiteSpec2.Spec)
ss.Finish()
})
assert.Must(t).False(stub.IsFailed)
assert.Equal(t, []string{"Test", "AfterAll"}, order, "expected to only run once, in the real spec execution")
}

func TestSpec_AfterAll_failIfDefinedAfterTestCases(t *testing.T) {
stub := &doubles.TB{}
sandbox.Run(func() {
s := testcase.NewSpec(stub)
s.Test(``, func(t *testcase.T) {})
s.AfterAll(func(tb testing.TB) {})
s.Test(``, func(t *testcase.T) {})
s.Finish()
})
assert.Must(t).True(stub.IsFailed)
}

0 comments on commit b915aa3

Please sign in to comment.