Про параметризованные тесты

Параметризованные тесты - это практика написания тестов, когда один и тот же тест выполняется на разных наборах входных данных. Ожидаемый результат при этом может как задаваться во входных данных, так и быть заранее известным. Для наглядности приведу пример из документации к junit4:

@RunWith(Parameterized.class)
public class FibonacciTest {
    @Parameters
    public static Collection<Object[]> data() {
        return Arrays.asList(new Object[][] {     
                 { 0, 0 }, 
                 { 1, 1 }, 
                 { 2, 1 }, 
                 { 3, 2 }, 
                 { 4, 3 }, 
                 { 5, 5 }, 
                 { 6, 8 }  
           });
    }

    private int fInput;

    private int fExpected;

    public FibonacciTest(int input, int expected) {
        this.fInput = input;
        this.fExpected = expected;
    }

    @Test
    public void test() {
        assertEquals(fExpected, Fibonacci.compute(fInput));
    }
}

Проблема в том, что примеры, которые приводятся в статьях, обычно предлагают протестировать либо что-то очень простое, либо математическую функцию. Либо и то и другое сразу, например, генерацию ряда Фибоначчи. Но даже в этом простейшем примере я вижу проблему. Читая такой тест, мне приходится прикладывать усилия, чтобы понять, что это за набор входных данных, почему он именно такой, преобразуется ли он внутри теста, и если да - то как именно. Код тестов должен помогать мне понять, как работает тестируемая функция. Вместо этого мне приходится разбираться как работает код тестов.

С появлением Go, стюардессу откопали параметризованные тесты получили вторую жизнь. Они (google) даже зачем-то придумали свой термин - “табличные тесты”. В каждой второй статье про лучшие практики тестирования в Go, есть пункт про “табличные тесты” (пруф раз, два, три). Да и в самом Go таких тестов хватает, вот пример из исходников менеджера зависимостей dep полный код теста на гитхабе):

var pathDeductionFixtures = map[string][]pathDeductionFixture{
	"github": {
		{
			in:   "github.com/sdboyer/gps",
			root: "github.com/sdboyer/gps",
			mb: maybeSources{
				maybeGitSource{url: mkurl("https://github.com/sdboyer/gps")},
				maybeGitSource{url: mkurl("ssh://git@github.com/sdboyer/gps")},
				maybeGitSource{url: mkurl("git://github.com/sdboyer/gps")},
				maybeGitSource{url: mkurl("http://github.com/sdboyer/gps")},
			},
		},
		{
			in:   "github.com/sdboyer/gps/foo",
			root: "github.com/sdboyer/gps",
			mb: maybeSources{
				maybeGitSource{url: mkurl("https://github.com/sdboyer/gps")},
				maybeGitSource{url: mkurl("ssh://git@github.com/sdboyer/gps")},
				maybeGitSource{url: mkurl("git://github.com/sdboyer/gps")},
				maybeGitSource{url: mkurl("http://github.com/sdboyer/gps")},
			},
		},
		{
			// TODO(sdboyer) is this a problem for enforcing uniqueness? do we
			// need to collapse these extensions?
			in:   "github.com/sdboyer/gps.git/foo",
			root: "github.com/sdboyer/gps.git",
			mb: maybeSources{
				maybeGitSource{url: mkurl("https://github.com/sdboyer/gps.git")},
				maybeGitSource{url: mkurl("ssh://git@github.com/sdboyer/gps.git")},
				maybeGitSource{url: mkurl("git://github.com/sdboyer/gps.git")},
				maybeGitSource{url: mkurl("http://github.com/sdboyer/gps.git")},
			},
		},
		{
			in:   "git@github.com:sdboyer/gps",
			root: "github.com/sdboyer/gps",
			mb: maybeSources{
				maybeGitSource{url: mkurl("ssh://git@github.com/sdboyer/gps")},
			},
		},
		{
			in:   "https://github.com/sdboyer/gps",
			root: "github.com/sdboyer/gps",
			mb: maybeSources{
				maybeGitSource{url: mkurl("https://github.com/sdboyer/gps")},
			},
		},
		{
			in:   "https://github.com/sdboyer/gps/foo/bar",
			root: "github.com/sdboyer/gps",
			mb: maybeSources{
				maybeGitSource{url: mkurl("https://github.com/sdboyer/gps")},
			},
		},
		{
			in:   "github.com/sdboyer-/gps/foo",
			root: "github.com/sdboyer-/gps",
			mb: maybeSources{
				maybeGitSource{url: mkurl("https://github.com/sdboyer-/gps")},
				maybeGitSource{url: mkurl("ssh://git@github.com/sdboyer-/gps")},
				maybeGitSource{url: mkurl("git://github.com/sdboyer-/gps")},
				maybeGitSource{url: mkurl("http://github.com/sdboyer-/gps")},
			},
		},
		{
			in:   "github.com/a/gps/foo",
			root: "github.com/a/gps",
			mb: maybeSources{
				maybeGitSource{url: mkurl("https://github.com/a/gps")},
				maybeGitSource{url: mkurl("ssh://git@github.com/a/gps")},
				maybeGitSource{url: mkurl("git://github.com/a/gps")},
				maybeGitSource{url: mkurl("http://github.com/a/gps")},
			},
		},
		// some invalid github username patterns
		{
			in:   "github.com/-sdboyer/gps/foo",
			rerr: errors.New("github.com/-sdboyer/gps/foo is not a valid path for a source on github.com"),
		},
		{
			in:   "github.com/sdbo.yer/gps/foo",
			rerr: errors.New("github.com/sdbo.yer/gps/foo is not a valid path for a source on github.com"),
		},
		{
			in:   "github.com/sdbo_yer/gps/foo",
			rerr: errors.New("github.com/sdbo_yer/gps/foo is not a valid path for a source on github.com"),
		},
		// Regression - gh does allow two-letter usernames
		{
			in:   "github.com/kr/pretty",
			root: "github.com/kr/pretty",
			mb: maybeSources{
				maybeGitSource{url: mkurl("https://github.com/kr/pretty")},
				maybeGitSource{url: mkurl("ssh://git@github.com/kr/pretty")},
				maybeGitSource{url: mkurl("git://github.com/kr/pretty")},
				maybeGitSource{url: mkurl("http://github.com/kr/pretty")},
			},
		},
	},
	"gopkg.in": {
		{
			in:   "gopkg.in/sdboyer/gps.v0",
			root: "gopkg.in/sdboyer/gps.v0",
			mb: maybeSources{
				maybeGopkginSource{opath: "gopkg.in/sdboyer/gps.v0", url: mkurl("https://github.com/sdboyer/gps"), major: 0},
				maybeGopkginSource{opath: "gopkg.in/sdboyer/gps.v0", url: mkurl("http://github.com/sdboyer/gps"), major: 0},
			},
		},
		{
			in:   "gopkg.in/sdboyer/gps.v0/foo",
			root: "gopkg.in/sdboyer/gps.v0",
			mb: maybeSources{
				maybeGopkginSource{opath: "gopkg.in/sdboyer/gps.v0", url: mkurl("https://github.com/sdboyer/gps"), major: 0},
				maybeGopkginSource{opath: "gopkg.in/sdboyer/gps.v0", url: mkurl("http://github.com/sdboyer/gps"), major: 0},
			},
		},
		{
			in:   "gopkg.in/sdboyer/gps.v1/foo/bar",
			root: "gopkg.in/sdboyer/gps.v1",
			mb: maybeSources{
				maybeGopkginSource{opath: "gopkg.in/sdboyer/gps.v1", url: mkurl("https://github.com/sdboyer/gps"), major: 1},
				maybeGopkginSource{opath: "gopkg.in/sdboyer/gps.v1", url: mkurl("http://github.com/sdboyer/gps"), major: 1},
			},
		},
		{
			in:   "gopkg.in/yaml.v1",
			root: "gopkg.in/yaml.v1",
			mb: maybeSources{
				maybeGopkginSource{opath: "gopkg.in/yaml.v1", url: mkurl("https://github.com/go-yaml/yaml"), major: 1},
				maybeGopkginSource{opath: "gopkg.in/yaml.v1", url: mkurl("http://github.com/go-yaml/yaml"), major: 1},
			},
		},
		{
			in:   "gopkg.in/yaml.v1/foo/bar",
			root: "gopkg.in/yaml.v1",
			mb: maybeSources{

				maybeGopkginSource{opath: "gopkg.in/yaml.v1", url: mkurl("https://github.com/go-yaml/yaml"), major: 1},
				maybeGopkginSource{opath: "gopkg.in/yaml.v1", url: mkurl("http://github.com/go-yaml/yaml"), major: 1},
			},
		},
		{
			in:   "gopkg.in/inf.v0",
			root: "gopkg.in/inf.v0",
			mb: maybeSources{
				maybeGopkginSource{opath: "gopkg.in/inf.v0", url: mkurl("https://github.com/go-inf/inf"), major: 0},
				maybeGopkginSource{opath: "gopkg.in/inf.v0", url: mkurl("http://github.com/go-inf/inf"), major: 0},
			},
		},
		{
			// gopkg.in only allows specifying major version in import path
			in:   "gopkg.in/yaml.v1.2",
			rerr: errors.New("gopkg.in/yaml.v1.2 is not a valid import path; gopkg.in only allows major versions (\"v1\" instead of \"v1.2\")"),
		},
	},
	"jazz": {
		// IBM hub devops services - fixtures borrowed from go get
		{
			in:   "hub.jazz.net/git/user1/pkgname",
			root: "hub.jazz.net/git/user1/pkgname",
			mb: maybeSources{
				maybeGitSource{url: mkurl("https://hub.jazz.net/git/user1/pkgname")},
			},
		},
		{
			in:   "hub.jazz.net/git/user1/pkgname/submodule/submodule/submodule",
			root: "hub.jazz.net/git/user1/pkgname",
			mb: maybeSources{
				maybeGitSource{url: mkurl("https://hub.jazz.net/git/user1/pkgname")},
			},
		},
		{
			in:   "hub.jazz.net/someotherprefix",
			rerr: errors.New("hub.jazz.net/someotherprefix is not a valid path for a source on hub.jazz.net"),
		},
		{
			in:   "hub.jazz.net/someotherprefix/user1/packagename",
			rerr: errors.New("hub.jazz.net/someotherprefix/user1/packagename is not a valid path for a source on hub.jazz.net"),
		},
		// Spaces are not valid in user names or package names
		{
			in:   "hub.jazz.net/git/User 1/pkgname",
			rerr: errors.New("hub.jazz.net/git/User 1/pkgname is not a valid path for a source on hub.jazz.net"),
		},
		{
			in:   "hub.jazz.net/git/user1/pkg name",
			rerr: errors.New("hub.jazz.net/git/user1/pkg name is not a valid path for a source on hub.jazz.net"),
		},
		// Dots are not valid in user names
		{
			in:   "hub.jazz.net/git/user.1/pkgname",
			rerr: errors.New("hub.jazz.net/git/user.1/pkgname is not a valid path for a source on hub.jazz.net"),
		},
		{
			in:   "hub.jazz.net/git/user1/pkg.name",
			root: "hub.jazz.net/git/user1/pkg.name",
			mb: maybeSources{
				maybeGitSource{url: mkurl("https://hub.jazz.net/git/user1/pkg.name")},
			},
		},
		// User names cannot have uppercase letters
		{
			in:   "hub.jazz.net/git/USER/pkgname",
			rerr: errors.New("hub.jazz.net/git/USER/pkgname is not a valid path for a source on hub.jazz.net"),
		},
	},
	"bitbucket": {
		{
			in:   "bitbucket.org/sdboyer/reporoot",
			root: "bitbucket.org/sdboyer/reporoot",
			mb: maybeSources{
				maybeHgSource{url: mkurl("https://bitbucket.org/sdboyer/reporoot")},
				maybeHgSource{url: mkurl("ssh://hg@bitbucket.org/sdboyer/reporoot")},
				maybeHgSource{url: mkurl("http://bitbucket.org/sdboyer/reporoot")},
				maybeGitSource{url: mkurl("https://bitbucket.org/sdboyer/reporoot")},
				maybeGitSource{url: mkurl("ssh://git@bitbucket.org/sdboyer/reporoot")},
				maybeGitSource{url: mkurl("git://bitbucket.org/sdboyer/reporoot")},
				maybeGitSource{url: mkurl("http://bitbucket.org/sdboyer/reporoot")},
			},
		},
		{
			in:   "bitbucket.org/sdboyer/reporoot/foo/bar",
			root: "bitbucket.org/sdboyer/reporoot",
			mb: maybeSources{
				maybeHgSource{url: mkurl("https://bitbucket.org/sdboyer/reporoot")},
				maybeHgSource{url: mkurl("ssh://hg@bitbucket.org/sdboyer/reporoot")},
				maybeHgSource{url: mkurl("http://bitbucket.org/sdboyer/reporoot")},
				maybeGitSource{url: mkurl("https://bitbucket.org/sdboyer/reporoot")},
				maybeGitSource{url: mkurl("ssh://git@bitbucket.org/sdboyer/reporoot")},
				maybeGitSource{url: mkurl("git://bitbucket.org/sdboyer/reporoot")},
				maybeGitSource{url: mkurl("http://bitbucket.org/sdboyer/reporoot")},
			},
		},
		{
			in:   "https://bitbucket.org/sdboyer/reporoot/foo/bar",
			root: "bitbucket.org/sdboyer/reporoot",
			mb: maybeSources{
				maybeHgSource{url: mkurl("https://bitbucket.org/sdboyer/reporoot")},
				maybeGitSource{url: mkurl("https://bitbucket.org/sdboyer/reporoot")},
			},
		},
		// Less standard behaviors possible due to the hg/git ambiguity
		{
			in:   "bitbucket.org/sdboyer/reporoot.git",
			root: "bitbucket.org/sdboyer/reporoot.git",
			mb: maybeSources{
				maybeGitSource{url: mkurl("https://bitbucket.org/sdboyer/reporoot.git")},
				maybeGitSource{url: mkurl("ssh://git@bitbucket.org/sdboyer/reporoot.git")},
				maybeGitSource{url: mkurl("git://bitbucket.org/sdboyer/reporoot.git")},
				maybeGitSource{url: mkurl("http://bitbucket.org/sdboyer/reporoot.git")},
			},
		},
		{
			in:   "git@bitbucket.org:sdboyer/reporoot.git",
			root: "bitbucket.org/sdboyer/reporoot.git",
			mb: maybeSources{
				maybeGitSource{url: mkurl("ssh://git@bitbucket.org/sdboyer/reporoot.git")},
			},
		},
		{
			in:   "bitbucket.org/sdboyer/reporoot.hg",
			root: "bitbucket.org/sdboyer/reporoot.hg",
			mb: maybeSources{
				maybeHgSource{url: mkurl("https://bitbucket.org/sdboyer/reporoot.hg")},
				maybeHgSource{url: mkurl("ssh://hg@bitbucket.org/sdboyer/reporoot.hg")},
				maybeHgSource{url: mkurl("http://bitbucket.org/sdboyer/reporoot.hg")},
			},
		},
		{
			in:   "hg@bitbucket.org:sdboyer/reporoot",
			root: "bitbucket.org/sdboyer/reporoot",
			mb: maybeSources{
				maybeHgSource{url: mkurl("ssh://hg@bitbucket.org/sdboyer/reporoot")},
			},
		},
		{
			in:     "git://bitbucket.org/sdboyer/reporoot.hg",
			root:   "bitbucket.org/sdboyer/reporoot.hg",
			srcerr: errors.New("git is not a valid scheme for accessing an hg repository"),
		},
	},
	"launchpad": {
		// tests for launchpad, mostly bazaar
		// TODO(sdboyer) need more tests to deal w/launchpad's oddities
		{
			in:   "launchpad.net/govcstestbzrrepo",
			root: "launchpad.net/govcstestbzrrepo",
			mb: maybeSources{
				maybeBzrSource{url: mkurl("https://launchpad.net/govcstestbzrrepo")},
				maybeBzrSource{url: mkurl("bzr+ssh://launchpad.net/govcstestbzrrepo")},
				maybeBzrSource{url: mkurl("bzr://launchpad.net/govcstestbzrrepo")},
				maybeBzrSource{url: mkurl("http://launchpad.net/govcstestbzrrepo")},
			},
		},
		{
			in:   "launchpad.net/govcstestbzrrepo/foo/bar",
			root: "launchpad.net/govcstestbzrrepo",
			mb: maybeSources{
				maybeBzrSource{url: mkurl("https://launchpad.net/govcstestbzrrepo")},
				maybeBzrSource{url: mkurl("bzr+ssh://launchpad.net/govcstestbzrrepo")},
				maybeBzrSource{url: mkurl("bzr://launchpad.net/govcstestbzrrepo")},
				maybeBzrSource{url: mkurl("http://launchpad.net/govcstestbzrrepo")},
			},
		},
		{
			in:   "launchpad.net/repo root",
			rerr: errors.New("launchpad.net/repo root is not a valid path for a source on launchpad.net"),
		},
	},
	"git.launchpad": {
		{
			in:   "git.launchpad.net/reporoot",
			root: "git.launchpad.net/reporoot",
			mb: maybeSources{
				maybeGitSource{url: mkurl("https://git.launchpad.net/reporoot")},
				maybeGitSource{url: mkurl("ssh://git.launchpad.net/reporoot")},
				maybeGitSource{url: mkurl("git://git.launchpad.net/reporoot")},
				maybeGitSource{url: mkurl("http://git.launchpad.net/reporoot")},
			},
		},
		{
			in:   "git.launchpad.net/reporoot/foo/bar",
			root: "git.launchpad.net/reporoot",
			mb: maybeSources{
				maybeGitSource{url: mkurl("https://git.launchpad.net/reporoot")},
				maybeGitSource{url: mkurl("ssh://git.launchpad.net/reporoot")},
				maybeGitSource{url: mkurl("git://git.launchpad.net/reporoot")},
				maybeGitSource{url: mkurl("http://git.launchpad.net/reporoot")},
			},
		},
		{
			in:   "git.launchpad.net/repo root",
			rerr: errors.New("git.launchpad.net/repo root is not a valid path for a source on git.launchpad.net"),
		},
	},
	"apache": {
		{
			in:   "git.apache.org/package-name.git",
			root: "git.apache.org/package-name.git",
			mb: maybeSources{
				maybeGitSource{url: mkurl("https://git.apache.org/package-name.git")},
				maybeGitSource{url: mkurl("ssh://git.apache.org/package-name.git")},
				maybeGitSource{url: mkurl("git://git.apache.org/package-name.git")},
				maybeGitSource{url: mkurl("http://git.apache.org/package-name.git")},
			},
		},
		{
			in:   "git.apache.org/package-name.git/foo/bar",
			root: "git.apache.org/package-name.git",
			mb: maybeSources{
				maybeGitSource{url: mkurl("https://git.apache.org/package-name.git")},
				maybeGitSource{url: mkurl("ssh://git.apache.org/package-name.git")},
				maybeGitSource{url: mkurl("git://git.apache.org/package-name.git")},
				maybeGitSource{url: mkurl("http://git.apache.org/package-name.git")},
			},
		},
	},
	"vcsext": {
		// VCS extension-based syntax
		{
			in:   "foobar.com/baz.git",
			root: "foobar.com/baz.git",
			mb: maybeSources{
				maybeGitSource{url: mkurl("https://foobar.com/baz.git")},
				maybeGitSource{url: mkurl("ssh://foobar.com/baz.git")},
				maybeGitSource{url: mkurl("git://foobar.com/baz.git")},
				maybeGitSource{url: mkurl("http://foobar.com/baz.git")},
			},
		},
		{
			in:   "foobar.com/baz.git/extra/path",
			root: "foobar.com/baz.git",
			mb: maybeSources{
				maybeGitSource{url: mkurl("https://foobar.com/baz.git")},
				maybeGitSource{url: mkurl("ssh://foobar.com/baz.git")},
				maybeGitSource{url: mkurl("git://foobar.com/baz.git")},
				maybeGitSource{url: mkurl("http://foobar.com/baz.git")},
			},
		},
		{
			in:   "foobar.com/baz.bzr",
			root: "foobar.com/baz.bzr",
			mb: maybeSources{
				maybeBzrSource{url: mkurl("https://foobar.com/baz.bzr")},
				maybeBzrSource{url: mkurl("bzr+ssh://foobar.com/baz.bzr")},
				maybeBzrSource{url: mkurl("bzr://foobar.com/baz.bzr")},
				maybeBzrSource{url: mkurl("http://foobar.com/baz.bzr")},
			},
		},
		{
			in:   "foo-bar.com/baz.hg",
			root: "foo-bar.com/baz.hg",
			mb: maybeSources{
				maybeHgSource{url: mkurl("https://foo-bar.com/baz.hg")},
				maybeHgSource{url: mkurl("ssh://foo-bar.com/baz.hg")},
				maybeHgSource{url: mkurl("http://foo-bar.com/baz.hg")},
			},
		},
		{
			in:   "git@foobar.com:baz.git",
			root: "foobar.com/baz.git",
			mb: maybeSources{
				maybeGitSource{url: mkurl("ssh://git@foobar.com/baz.git")},
			},
		},
		{
			in:   "bzr+ssh://foobar.com/baz.bzr",
			root: "foobar.com/baz.bzr",
			mb: maybeSources{
				maybeBzrSource{url: mkurl("bzr+ssh://foobar.com/baz.bzr")},
			},
		},
		{
			in:   "ssh://foobar.com/baz.bzr",
			root: "foobar.com/baz.bzr",
			mb: maybeSources{
				maybeBzrSource{url: mkurl("ssh://foobar.com/baz.bzr")},
			},
		},
		{
			in:   "https://foobar.com/baz.hg",
			root: "foobar.com/baz.hg",
			mb: maybeSources{
				maybeHgSource{url: mkurl("https://foobar.com/baz.hg")},
			},
		},
		{
			in:     "git://foobar.com/baz.hg",
			root:   "foobar.com/baz.hg",
			srcerr: errors.New("git is not a valid scheme for accessing hg repositories (path foobar.com/baz.hg)"),
		},
		// who knows why anyone would do this, but having a second vcs ext
		// shouldn't throw us off - only the first one counts
		{
			in:   "foobar.com/baz.git/quark/quizzle.bzr/quorum",
			root: "foobar.com/baz.git",
			mb: maybeSources{
				maybeGitSource{url: mkurl("https://foobar.com/baz.git")},
				maybeGitSource{url: mkurl("ssh://foobar.com/baz.git")},
				maybeGitSource{url: mkurl("git://foobar.com/baz.git")},
				maybeGitSource{url: mkurl("http://foobar.com/baz.git")},
			},
		},
	},
	"vanity": {
		// Vanity imports
		{
			in:   "golang.org/x/exp",
			root: "golang.org/x/exp",
			mb: maybeSources{
				maybeGitSource{url: mkurl("https://go.googlesource.com/exp")},
			},
		},
		{
			in:   "golang.org/x/exp/inotify",
			root: "golang.org/x/exp",
			mb: maybeSources{
				maybeGitSource{url: mkurl("https://go.googlesource.com/exp")},
			},
		},
		{
			in:   "golang.org/x/net/html",
			root: "golang.org/x/net",
			mb: maybeSources{
				maybeGitSource{url: mkurl("https://go.googlesource.com/net")},
			},
		},
	},
}

func TestDeduceFromPath(t *testing.T) {
	do := func(typ string, fixtures []pathDeductionFixture, t *testing.T) {
		t.Run(typ, func(t *testing.T) {
			t.Parallel()

			var deducer pathDeducer
			switch typ {
			case "github":
				deducer = githubDeducer{regexp: ghRegex}
			case "gopkg.in":
				deducer = gopkginDeducer{regexp: gpinNewRegex}
			case "jazz":
				deducer = jazzDeducer{regexp: jazzRegex}
			case "bitbucket":
				deducer = bitbucketDeducer{regexp: bbRegex}
			case "launchpad":
				deducer = launchpadDeducer{regexp: lpRegex}
			case "git.launchpad":
				deducer = launchpadGitDeducer{regexp: glpRegex}
			case "apache":
				deducer = apacheDeducer{regexp: apacheRegex}
			case "vcsext":
				deducer = vcsExtensionDeducer{regexp: vcsExtensionRegex}
			default:
				// Should just be the vanity imports, which we do elsewhere
				t.Log("skipping")
				t.SkipNow()
			}

			printmb := func(mb maybeSources) string {
				var buf bytes.Buffer
				fmt.Fprintf(&buf, "%v maybeSources:", len(mb))
				for _, elem := range mb {
					fmt.Fprintf(&buf, "\n\t\t%s", elem)
				}
				return buf.String()
			}

			for _, fix := range fixtures {
				fix := fix
				t.Run(fix.in, func(t *testing.T) {
					t.Parallel()
					u, in, uerr := normalizeURI(fix.in)
					if uerr != nil {
						if fix.rerr == nil {
							t.Errorf("bad input URI %s", uerr)
						}
						t.SkipNow()
					}

					root, rerr := deducer.deduceRoot(in)
					if fix.rerr != nil {
						if rerr == nil {
							t.Errorf("Expected error on deducing root, got none:\n\t(WNT) %s", fix.rerr)
						} else if fix.rerr.Error() != rerr.Error() {
							t.Errorf("Got unexpected error on deducing root:\n\t(GOT) %s\n\t(WNT) %s", rerr, fix.rerr)
						}
					} else if rerr != nil {
						t.Errorf("Got unexpected error on deducing root:\n\t(GOT) %s", rerr)
					} else if root != fix.root {
						t.Errorf("Deducer did not return expected root:\n\t(GOT) %s\n\t(WNT) %s", root, fix.root)
					}

					mb, mberr := deducer.deduceSource(in, u)
					if fix.srcerr != nil {
						if mberr == nil {
							t.Errorf("Expected error on deducing source, got none:\n\t(WNT) %s", fix.srcerr)
						} else if fix.srcerr.Error() != mberr.Error() {
							t.Errorf("Got unexpected error on deducing source:\n\t(GOT) %s\n\t(WNT) %s", mberr, fix.srcerr)
						}
					} else if mberr != nil {
						// don't complain the fix already expected an rerr
						if fix.rerr == nil {
							t.Errorf("Got unexpected error on deducing source:\n\t(GOT) %s", mberr)
						}
					} else if !reflect.DeepEqual(mb, fix.mb) {
						if mb == nil {
							t.Errorf("Deducer returned source maybes, but none expected:\n\t(GOT) (none)\n\t(WNT) %s", printmb(fix.mb))
						} else if fix.mb == nil {
							t.Errorf("Deducer returned source maybes, but none expected:\n\t(GOT) %s\n\t(WNT) (none)", printmb(mb))
						} else {
							t.Errorf("Deducer did not return expected source:\n\t(GOT) %s\n\t(WNT) %s", printmb(mb), printmb(fix.mb))
						}
					}
				})
			}
		})
	}
	runSet := func(t *testing.T) {
		for typ, fixtures := range pathDeductionFixtures {
			do(typ, fixtures, t)
		}
	}
	t.Run("first", runSet)

	// Run the test set twice to ensure results are correct for both cached
	// and uncached deductions.
	t.Run("second", runSet)
}

Правда ведь, даже не хочется вчитываться в код такого теста?

Другой пример злоупотребления параметризацией - это end-2-end тесты. Классика жанра - страница логина. Протестируем что нельзя залогиниться с несуществующим юзером, с пустым юзером, с пустым паролем, с неправильным паролем и т.д. Для меня это сразу тревожный звоночек - подобная вариативность должна проверяться в unit- тестах (в крайнем случае - в функциональных), но никак не в e2e. А поскольку подготовить тестируемый кусочек “на нижних” уровнях сильно проще, вспомогательного кода будет меньше и параметризованный тест писать тоже смысла особо не будет.

comments powered by Disqus