Skip to content

Commit a9a08b5

Browse files
committed
testing: Add T.ExpectFail()
ExpectFail() signals a test is expected to fail. The PASS/FAIL result is inverted; passing tests fail, failing tests pass.
1 parent 39a51a4 commit a9a08b5

File tree

2 files changed

+77
-14
lines changed

2 files changed

+77
-14
lines changed

src/testing/sub_test.go

+23
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,29 @@ func TestTRun(t *T) {
315315
f: func(t *T) {
316316
t.Skip()
317317
},
318+
}, {
319+
desc: "expect fail, get fail",
320+
ok: true,
321+
f: func(t *T) {
322+
t.ExpectFail()
323+
t.Fail()
324+
},
325+
}, {
326+
desc: "expect panic fail, get panic fail",
327+
ok: true,
328+
f: func(t *T) {
329+
t.ExpectFail()
330+
panic("expected panic")
331+
},
332+
}, {
333+
desc: "expect fail, no fail",
334+
ok: false,
335+
output: `
336+
--- FAIL: expect fail, no fail (N.NNs)
337+
testing.go:NNN: expected failure; test did not fail`,
338+
f: func(t *T) {
339+
t.ExpectFail()
340+
},
318341
}, {
319342
desc: "subtest calls error on parent",
320343
ok: false,

src/testing/testing.go

+54-14
Original file line numberDiff line numberDiff line change
@@ -300,14 +300,15 @@ const maxStackLen = 50
300300
// common holds the elements common between T and B and
301301
// captures common methods such as Errorf.
302302
type common struct {
303-
mu sync.RWMutex // guards this group of fields
304-
output []byte // Output generated by test or benchmark.
305-
w io.Writer // For flushToParent.
306-
ran bool // Test or benchmark (or one of its subtests) was executed.
307-
failed bool // Test or benchmark has failed.
308-
skipped bool // Test of benchmark has been skipped.
309-
done bool // Test is finished and all subtests have completed.
310-
helpers map[string]struct{} // functions to be skipped when writing file/line info
303+
mu sync.RWMutex // guards this group of fields
304+
output []byte // Output generated by test or benchmark.
305+
w io.Writer // For flushToParent.
306+
ran bool // Test or benchmark (or one of its subtests) was executed.
307+
failed bool // Test or benchmark has failed.
308+
expectFail bool // Test is expected to fail.
309+
skipped bool // Test or benchmark has been skipped.
310+
done bool // Test is finished and all subtests have completed.
311+
helpers map[string]struct{} // functions to be skipped when writing file/line info
311312

312313
chatty bool // A copy of the chatty flag.
313314
finished bool // Test function has completed.
@@ -526,8 +527,8 @@ var _ TB = (*B)(nil)
526527
//
527528
// A test ends when its Test function returns or calls any of the methods
528529
// FailNow, Fatal, Fatalf, SkipNow, Skip, or Skipf. Those methods, as well as
529-
// the Parallel method, must be called only from the goroutine running the
530-
// Test function.
530+
// the Parallel and ExpectFail methods, must be called only from the goroutine
531+
// running the Test function.
531532
//
532533
// The other reporting methods, such as the variations of Log and Error,
533534
// may be called simultaneously from multiple goroutines.
@@ -555,7 +556,8 @@ func (c *common) setRan() {
555556

556557
// Fail marks the function as having failed but continues execution.
557558
func (c *common) Fail() {
558-
if c.parent != nil {
559+
// Fail the parent if the failure is unexpected.
560+
if c.parent != nil && !c.expectFail {
559561
c.parent.Fail()
560562
}
561563
c.mu.Lock()
@@ -784,6 +786,22 @@ func (t *T) Parallel() {
784786
t.raceErrors += -race.Errors()
785787
}
786788

789+
// ExpectFail signals that this test is expected to fail. The PASS/FAIL result
790+
// is inverted; passing tests fail, failing tests pass. Use for bug reproduction
791+
// or documentation of unimplemented features.
792+
func (t *T) ExpectFail() {
793+
t.mu.Lock()
794+
defer t.mu.Unlock()
795+
t.expectFail = true
796+
}
797+
798+
// unfail resets a test failure. Used when the failure was expected.
799+
func (t *T) unfail() {
800+
t.mu.Lock()
801+
defer t.mu.Unlock()
802+
t.failed = false
803+
}
804+
787805
// An internal type but exported because it is cross-package; part of the implementation
788806
// of the "go test" command.
789807
type InternalTest struct {
@@ -825,9 +843,31 @@ func tRunner(t *T, fn func(t *T)) {
825843
}
826844
}
827845
if err != nil {
828-
t.Fail()
829-
t.report()
830-
panic(err)
846+
if t.expectFail {
847+
// Set failure, but do not die.
848+
t.Errorf("panic: %s", err)
849+
} else {
850+
t.Fail()
851+
t.report()
852+
panic(err)
853+
}
854+
}
855+
856+
if t.expectFail {
857+
if t.failed {
858+
// Test passes
859+
t.Log("expected failure; test failed")
860+
t.unfail()
861+
} else {
862+
t.Error("expected failure; test did not fail")
863+
// Fail parent because it was skipped earlier.
864+
if t.parent != nil {
865+
t.parent.Fail()
866+
}
867+
868+
// Increment because it was skipped.
869+
atomic.AddUint32(&numFailed, 1)
870+
}
831871
}
832872

833873
if len(t.sub) > 0 {

0 commit comments

Comments
 (0)