Skip to content

Commit 4b45ede

Browse files
committed
Add testingiface package
1 parent 2a7ad8d commit 4b45ede

File tree

5 files changed

+476
-0
lines changed

5 files changed

+476
-0
lines changed

internal/testingiface/doc.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright (c) HashiCorp, Inc.
2+
// SPDX-License-Identifier: MPL-2.0
3+
4+
// Package testingiface provides wrapper types compatible with the Go standard
5+
// library [testing] package. These wrappers are necessary for implementing
6+
// [testing] package helpers, since the Go standard library implementation is
7+
// not extensible and the existing code in this Go module is built on directly
8+
// interacting with [testing] functionality, such as calling [testing.T.Fatal].
9+
//
10+
// The [T] interface has all methods of the [testing.T] type and the [MockT]
11+
// type is a lightweight mock implementation of the [T] interface. There are a
12+
// collection of assertion helper functions such as:
13+
// - [ExpectFail]: That the test logic called the equivalent of
14+
// [testing.T.Error] or [testing.T.Fatal].
15+
// - [ExpectParallel]: That the test logic called the equivalent of
16+
// [testing.T.Parallel] and passed.
17+
// - [ExpectPass]: That the test logic did not call the equivalent of
18+
// [testing.T.Skip], since [testing] marks these tests as passing.
19+
// - [ExpectSkip]: That the test logic called the equivalent of
20+
// [testing.T.Skip].
21+
//
22+
// This code in this package is intentionally internal and should not be exposed
23+
// in the Go module API. It is compatible with the Go 1.17 [testing] package.
24+
// It replaces the archived github.com/mitchellh/go-testing-interface Go module,
25+
// but is implemented with different approaches that enable calls to behave
26+
// more closely to the Go standard library, such as calling [runtime.Goexit]
27+
// when skipping, and preserving any error/skip messaging.
28+
package testingiface

internal/testingiface/expect.go

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
// Copyright (c) HashiCorp, Inc.
2+
// SPDX-License-Identifier: MPL-2.0
3+
4+
package testingiface
5+
6+
import (
7+
"sync"
8+
)
9+
10+
// ExpectFailed provides a wrapper for test logic which should call any of the
11+
// following:
12+
// - [testing.T.Error]
13+
// - [testing.T.Errorf]
14+
// - [testing.T.Fatal]
15+
// - [testing.T.Fatalf]
16+
//
17+
// If none of those were called, the real [testing.T.Fatal] is called to fail
18+
// the test.
19+
func ExpectFail(t T, logic func(*MockT)) {
20+
t.Helper()
21+
22+
mockT := &MockT{}
23+
24+
var wg sync.WaitGroup
25+
wg.Add(1)
26+
27+
go func() {
28+
defer wg.Done()
29+
30+
logic(mockT)
31+
}()
32+
33+
wg.Wait()
34+
35+
if mockT.Failed() {
36+
return
37+
}
38+
39+
t.Fatal("expected test failure")
40+
}
41+
42+
// ExpectParallel provides a wrapper for test logic which should call the
43+
// [testing.T.Parallel] method. If it doesn't, the real [testing.T.Fatal] is
44+
// called.
45+
func ExpectParallel(t T, logic func(*MockT)) {
46+
t.Helper()
47+
48+
mockT := &MockT{}
49+
50+
var wg sync.WaitGroup
51+
wg.Add(1)
52+
53+
go func() {
54+
defer wg.Done()
55+
56+
logic(mockT)
57+
}()
58+
59+
wg.Wait()
60+
61+
if mockT.Failed() {
62+
t.Fatalf("unexpected test failure: %s", mockT.LastError())
63+
}
64+
65+
if mockT.Skipped() {
66+
t.Fatalf("unexpected test skip: %s", mockT.LastSkipped())
67+
}
68+
69+
if mockT.IsParallel() {
70+
return
71+
}
72+
73+
t.Fatal("expected test parallel")
74+
}
75+
76+
// ExpectPass provides a wrapper for test logic which should not call any of the
77+
// following, which would mark the real test as passing:
78+
// - [testing.T.Skip]
79+
// - [testing.T.Skipf]
80+
//
81+
// If one of those were called, the real [testing.T.Fatal] is called to fail
82+
// the test. This is only necessary to check for false positives with skipped
83+
// tests.
84+
func ExpectPass(t T, logic func(*MockT)) {
85+
t.Helper()
86+
87+
mockT := &MockT{}
88+
89+
var wg sync.WaitGroup
90+
wg.Add(1)
91+
92+
go func() {
93+
defer wg.Done()
94+
95+
logic(mockT)
96+
}()
97+
98+
wg.Wait()
99+
100+
if mockT.Failed() {
101+
t.Fatalf("unexpected test failure: %s", mockT.LastError())
102+
}
103+
104+
if mockT.Skipped() {
105+
t.Fatalf("unexpected test skip: %s", mockT.LastSkipped())
106+
}
107+
108+
// test passed as expected
109+
}
110+
111+
// ExpectSkip provides a wrapper for test logic which should call any of the
112+
// following:
113+
// - [testing.T.Skip]
114+
// - [testing.T.Skipf]
115+
//
116+
// If none of those were called, the real [testing.T.Fatal] is called to fail
117+
// the test.
118+
func ExpectSkip(t T, logic func(*MockT)) {
119+
t.Helper()
120+
121+
mockT := &MockT{}
122+
123+
var wg sync.WaitGroup
124+
wg.Add(1)
125+
126+
go func() {
127+
defer wg.Done()
128+
129+
logic(mockT)
130+
}()
131+
132+
wg.Wait()
133+
134+
if mockT.Failed() {
135+
t.Fatalf("unexpected test failure: %s", mockT.LastError())
136+
}
137+
138+
if mockT.Skipped() {
139+
return
140+
}
141+
142+
t.Fatal("test passed, expected test skip")
143+
}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
// Copyright (c) HashiCorp, Inc.
2+
// SPDX-License-Identifier: MPL-2.0
3+
4+
package testingiface_test
5+
6+
import (
7+
"testing"
8+
9+
"github.com/hashicorp/terraform-plugin-testing/internal/testingiface"
10+
)
11+
12+
func TestExpectFail(t *testing.T) {
13+
t.Parallel()
14+
15+
testingiface.ExpectPass(t, func(mockT1 *testingiface.MockT) {
16+
testingiface.ExpectFail(mockT1, func(mockT2 *testingiface.MockT) {
17+
mockT2.Fatal("test fatal")
18+
})
19+
})
20+
21+
testingiface.ExpectFail(t, func(mockT1 *testingiface.MockT) {
22+
testingiface.ExpectFail(mockT1, func(_ *testingiface.MockT) {
23+
// intentionally no test error or test skip
24+
})
25+
})
26+
27+
testingiface.ExpectFail(t, func(mockT1 *testingiface.MockT) {
28+
testingiface.ExpectFail(mockT1, func(mockT2 *testingiface.MockT) {
29+
mockT2.Skip("test skip")
30+
})
31+
})
32+
}
33+
34+
func TestExpectParallel(t *testing.T) {
35+
t.Parallel()
36+
37+
testingiface.ExpectFail(t, func(mockT1 *testingiface.MockT) {
38+
testingiface.ExpectParallel(mockT1, func(mockT2 *testingiface.MockT) {
39+
mockT2.Fatal("test fatal")
40+
})
41+
})
42+
43+
testingiface.ExpectFail(t, func(mockT1 *testingiface.MockT) {
44+
testingiface.ExpectParallel(mockT1, func(_ *testingiface.MockT) {
45+
// intentionally no test error or test skip
46+
})
47+
})
48+
49+
testingiface.ExpectPass(t, func(mockT1 *testingiface.MockT) {
50+
testingiface.ExpectParallel(mockT1, func(mockT2 *testingiface.MockT) {
51+
mockT2.Parallel()
52+
})
53+
})
54+
55+
testingiface.ExpectFail(t, func(mockT1 *testingiface.MockT) {
56+
testingiface.ExpectParallel(mockT1, func(mockT2 *testingiface.MockT) {
57+
mockT2.Skip("test skip")
58+
})
59+
})
60+
}
61+
62+
func TestExpectPass(t *testing.T) {
63+
t.Parallel()
64+
65+
testingiface.ExpectFail(t, func(mockT1 *testingiface.MockT) {
66+
testingiface.ExpectPass(mockT1, func(mockT2 *testingiface.MockT) {
67+
mockT2.Fatal("test fatal")
68+
})
69+
})
70+
71+
testingiface.ExpectPass(t, func(_ *testingiface.MockT) {
72+
// intentionally no test error or test skip
73+
})
74+
75+
testingiface.ExpectFail(t, func(mockT1 *testingiface.MockT) {
76+
testingiface.ExpectPass(mockT1, func(mockT2 *testingiface.MockT) {
77+
mockT2.Skip("test skip")
78+
})
79+
})
80+
}
81+
82+
func TestExpectSkip(t *testing.T) {
83+
t.Parallel()
84+
85+
testingiface.ExpectFail(t, func(mockT1 *testingiface.MockT) {
86+
testingiface.ExpectSkip(mockT1, func(mockT2 *testingiface.MockT) {
87+
mockT2.Fatal("test fatal")
88+
})
89+
})
90+
91+
testingiface.ExpectFail(t, func(mockT1 *testingiface.MockT) {
92+
testingiface.ExpectSkip(mockT1, func(_ *testingiface.MockT) {
93+
// intentionally no test error or test skip
94+
})
95+
})
96+
97+
testingiface.ExpectPass(t, func(mockT1 *testingiface.MockT) {
98+
testingiface.ExpectSkip(mockT1, func(mockT2 *testingiface.MockT) {
99+
mockT2.Skip("test skip")
100+
})
101+
})
102+
}

0 commit comments

Comments
 (0)