Go: proposal: testing: add a flag to detect unnecessary skips

Created on 18 Jun 2018  Â·  8Comments  Â·  Source: golang/go

Motivation

We often use (*testing.T).Skip to skip tests that fail due to known bugs in the Go toolchain, the standard library, or a specific platform where the test runs.

When the underlying bugs are fixed, it is important that we remove the skips to prevent regressions.

However, bugs are sometimes fixed without us noticing — especially if they are platform bugs. In such cases, it would be helpful to have some way to detect that situation short of editing and recompiling all of the tests in a project.

Here is a concrete example in the standard library: a t.Skip call that references an issue that was marked closed back in 2016 (#17065). Given how easy that call was to find, I expect that similarly outdated skips are pervasive.

Proposal

A new boolean flag for the testing package, tentatively named -test.unskip, with the corresponding go test flag -unskip. -test.unskip takes a regular expression, which is matched against the formatted arguments of t.Skip or t.Skipf or the most recent test log entry before t.SkipNow.

If a Skip matches the regular expression, the test does not stop its execution as usual: instead, it continues to run. If the skipped test passes, the test is recorded as unnecessarily-skipped and the binary will exit with a nonzero exit code. If the skipped test fails, the test log (and the exit code of the test binary) ignores the failure and proceeds as usual.

Comparison

Test frameworks in a few other languages provide an explicit mechanism for expecting a test to fail. For example, Python has @unittest.expectedFailure; Boost has an expected_failures decorator; RSpec has the pending keyword.

Proposal Proposal-Hold

Most helpful comment

I agree that having an "expected failure" mode would be helpful.

A potentially related issue I've encountered in the past is making sure that CI doesn't skip tests when it shouldn't. For example, suppose that you write your tests to be skipped if a SQL database isn't running, and you set up one as part of CI. There currently isn't a way to tell go test never to skip those tests, other than doing go test -v and manually checking the SKIP output lines from time to time.

All 8 comments

It's an interesting idea but I note that some tests are skipped not because they fail but because they impose excessive resource costs on some systems (beyond what -test.short blocks) or because they cause some systems to crash. So this will push us in the direction of adding additional ways to skip a test.

some tests are skipped not because they fail but because they impose excessive resource costs on some systems (beyond what -test.short blocks) or because they cause some systems to crash.

That sort of use-case is part of the reason I proposed a regular expression rather than a boolean flag.

However, it certainly remains a usability consideration: for example, we wouldn't want to encourage people to run -unskip=. on unfamiliar low-level code (or on machines that can't tolerate a crash or accidental forkbomb).

It would also be nice to handle flaky tests somehow. Some tests are skipped because they are flaky on some platforms. So the fact that they pass once doesn't indicate much one way or another.

For non-flaky tests I wonder if test.XFail("reason") would suffice to address this problem. It would run the test as usual but flip the meaning of the result: a failure is a pass, a pass is a failure. You mentioned this possibility in the original proposal; why not use it for Go?

I agree that having an "expected failure" mode would be helpful.

A potentially related issue I've encountered in the past is making sure that CI doesn't skip tests when it shouldn't. For example, suppose that you write your tests to be skipped if a SQL database isn't running, and you set up one as part of CI. There currently isn't a way to tell go test never to skip those tests, other than doing go test -v and manually checking the SKIP output lines from time to time.

For non-flaky tests I wonder if test.XFail("reason") would suffice to address this problem.

That would probably suffice going forward, but introduces a migration problem: existing tests already use Skip.

That said, migrating Skips to XFails is a one-time cost. I'd be happy enough with it, I suspect.

XFail sounds interesting on its own right, but it still doesn't handle Skip-for-flaky. Let's put this on hold and circle back to package testing at some point later, during the Go 2 library phase.

I had the same feature request and decided to implement it as a learning exercise and potential contribution. It does not require a new flag. It includes tests, but needs additional documentation: https://github.com/13rac1/go/commit/a9a08b59

Passing Test:

func TestFail(t *testing.T) {
    t.ExpectFail()
    t.Fail()
}

func TestFailNow(t *testing.T) {
    t.ExpectFail()
    t.FailNow()
}

func TestPanic(t *testing.T) {
    t.ExpectFail()
    panic("test")
}

Passing Results:

=== RUN   TestFail
--- PASS: TestFail (0.00s)
    testing.go:828: expected failure; test failed
=== RUN   TestFailNow
--- PASS: TestFailNow (0.00s)
    testing.go:828: expected failure; test failed
=== RUN   TestPanic
--- PASS: TestPanic (0.00s)
    testing.go:817: panic: test
    testing.go:828: expected failure; test failed
PASS

Failing Test:

func TestPass(t *testing.T) {
    t.ExpectFail()
    t.Log("pass")
}

Failing Results:

=== RUN   TestPass
--- FAIL: TestPass (0.00s)
    main_test.go:7: pass
    testing.go:831: expected failure; test did not fail
FAIL

edited SHA after rebase

Here's a neat example:
https://github.com/golang/go/blob/a813d3c788b4ec58032616e8d269ee65d1b10085/src/cmd/go/go_test.go#L4692-L4694

Added in CL 42533 (May 4, 2017), rendered obsolete in CL 46004 (June 16, 2017), and skipping the test needlessly on an entire OS ever since.

Was this page helpful?
0 / 5 - 0 ratings