datadriven

package module
v1.0.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 23, 2025 License: Apache-2.0 Imports: 17 Imported by: 211

README

Data-Driven Tests for Go

This repository implements an extension of Table-Driven Testing. Instead of building and iterating over a table in the test code, the input is further separated into files (or inline strings). For certain classes of tests, this can significantly reduce the friction involved in writing and reading these tests.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ClearResults

func ClearResults(path string) error

func RunTest

func RunTest(t *testing.T, path string, f func(t *testing.T, td *TestData) string)

RunTest invokes a data-driven test. The test cases are contained in a separate test file and are dynamically loaded, parsed, and executed by this testing framework. By convention, test files are typically located in a sub-directory called "testdata". Each test file has the following format:

<command>[,<command>...] [arg | arg=val | arg=(val1, val2, ...)]...
<input to the command>
----
<expected results>

The command input can contain blank lines. However, by default, the expected results cannot contain blank lines. This alternate syntax allows the use of blank lines:

<command>[,<command>...] [arg | arg=val | arg=(val1, val2, ...)]...
<input to the command>
----
----
<expected results>

<more expected results>
----
----

To execute data-driven tests, pass the path of the test file as well as a function which can interpret and execute whatever commands are present in the test file. The framework invokes the function, passing it information about the test case in a TestData struct.

The function must returns the actual results of the case, which RunTest() compares with the expected results. If the two are not equal, the test is marked to fail.

Note that RunTest() creates a sub-instance of testing.T for each directive in the input file. It is thus unsafe/invalid to call e.g. Fatal() or Skip() on the parent testing.T from inside the callback function. Use the provided testing.T instance instead.

It is possible for a test to test for an "expected error" as follows:

  • run the code to test
  • if an error occurs, report the detail of the error as actual output.
  • place the expected error details in the expected results in the input file.

It is also possible for a test to report an _unexpected_ test error by calling t.Error().

func RunTestAny added in v1.0.3

func RunTestAny(t testing.TB, path string, f func(t testing.TB, td *TestData) string)

RunTestAny is like RunTest but works over a testing.TB.

func RunTestFromString

func RunTestFromString(t *testing.T, input string, f func(t *testing.T, td *TestData) string)

RunTestFromString is a version of RunTest which takes the contents of a test directly.

func RunTestFromStringAny added in v1.0.3

func RunTestFromStringAny(t testing.TB, input string, f func(t testing.TB, td *TestData) string)

RunTestFromStringAny is like RunTestFromString but works with a testing.TB.

func Verbose

func Verbose() bool

Verbose returns true iff -datadriven-quiet was not passed.

func Walk

func Walk(t *testing.T, path string, f func(t *testing.T, path string))

Walk goes through all the files in a subdirectory, creating subtests to match the file hierarchy; for each "leaf" file, the given function is called.

This can be used in conjunction with RunTest. For example:

 datadriven.Walk(t, path, func (t *testing.T, path string) {
   // initialize per-test state
   datadriven.RunTest(t, path, func (t *testing.T, d *datadriven.TestData) string {
    // ...
   }
 }

Files:
  testdata/typing
  testdata/logprops/scan
  testdata/logprops/select

If path is "testdata/typing", the function is called once and no subtests are created.

If path is "testdata/logprops", the function is called two times, in separate subtests /scan, /select.

If path is "testdata", the function is called three times, in subtest hierarchy /typing, /logprops/scan, /logprops/select.

func WalkAny added in v1.0.3

func WalkAny(t testing.TB, path string, f func(t testing.TB, path string))

WalkAny is like Walk but works over a testing.TB.

Types

type CmdArg

type CmdArg struct {
	Key  string
	Vals []string
}

CmdArg contains information about an argument on the directive line. An argument is specified in one of the following forms:

  • argument
  • argument=value
  • argument=(values, ...)

func ParseLine

func ParseLine(line string) (cmd string, cmdArgs []CmdArg, err error)

ParseLine parses a datadriven directive line and returns the parsed command and CmdArgs.

An input directive line is a command optionally followed by a list of arguments. Arguments may or may not have values and are specified with one of the forms:

  • <argname> # No values.
  • <argname>=<value> # Single value.
  • <argname>=(<value1>, <value2>, ...) # Multiple values.

Note that in the last case, we allow the values to contain parens; the parsing will take nesting into account. For example:

cmd exprs=(a + (b + c), d + f)

is valid and produces the expected values for the argument.

func (CmdArg) ExpectNumVals added in v1.0.3

func (arg CmdArg) ExpectNumVals(t testing.TB, num int)

ExpectNumVals asserts that arg has exactly num values.

func (CmdArg) ExpectNumValsGE added in v1.0.3

func (arg CmdArg) ExpectNumValsGE(t testing.TB, minNum int)

ExpectNumValsGE asserts that arg has at least minNum values.

func (CmdArg) FirstVal added in v1.0.3

func (arg CmdArg) FirstVal(t testing.TB) string

FirstVal asserts that arg has at least one value and returns the first one.

func (CmdArg) Scan

func (arg CmdArg) Scan(t testing.TB, i int, dest interface{})

Scan attempts to parse the value at index i into the dest.

func (CmdArg) SingleVal added in v1.0.3

func (arg CmdArg) SingleVal(t testing.TB) string

SingleVal asserts that arg has exactly one value and returns it.

func (CmdArg) String

func (arg CmdArg) String() string

func (CmdArg) TwoVals added in v1.0.3

func (arg CmdArg) TwoVals(t testing.TB) (string, string)

TwoVals asserts that arg has exactly two values and returns them.

type TestData

type TestData struct {
	// Pos is a file:line prefix for the input test file, suitable for
	// inclusion in logs and error messages.
	Pos string

	// Cmd is the first string on the directive line (up to the first whitespace).
	Cmd string

	// CmdArgs contains the k/v arguments to the command.
	CmdArgs []CmdArg

	// Input is the text between the first directive line and the ---- separator.
	Input string

	// Expected is the value below the ---- separator. In most cases,
	// tests need not check this, and instead return their own actual
	// output.
	// This field is provided so that a test can perform an early return
	// with "return d.Expected" to signal that nothing has changed.
	Expected string

	// Rewrite is set if the test is being run with the -rewrite flag.
	Rewrite bool
}

TestData contains information about one data-driven test case that was parsed from the test file.

func (*TestData) Arg added in v1.0.3

func (td *TestData) Arg(key string) (arg CmdArg, ok bool)

Arg retrieves the first CmdArg matching the given key. The second return value indicates whether such an argument exists.

func (TestData) Fatalf

func (td TestData) Fatalf(tb testing.TB, format string, args ...interface{})

Fatalf wraps a fatal testing error with test file position information, so that it's easy to locate the source of the error.

Note: tb.Fatalf can also be used directly; the test file position information will be logged.

func (*TestData) FullCmd added in v1.0.3

func (td *TestData) FullCmd() string

FullCmd renders the command and the arguments.

func (*TestData) HasArg

func (td *TestData) HasArg(key string) bool

HasArg checks whether the CmdArgs array contains an entry for the given key.

func (TestData) Logf added in v1.0.3

func (td TestData) Logf(tb testing.TB, format string, args ...interface{})

Logf is a wrapper for tb.Logf which adds file position information, so that it's easy to locate the source of the log.

func (*TestData) MaybeScanArgs added in v1.0.3

func (td *TestData) MaybeScanArgs(t testing.TB, key string, dests ...interface{}) bool

MaybeScanArgs behaves identically to ScanArgs, except that if the arg does not exist it leaves the destinations unmodified and returns false. In all other cases it returns true.

func (*TestData) Retry added in v1.0.3

func (td *TestData) Retry(tb testing.TB, f func() string) string

Retry is used for tests that depend on background goroutines to finish work. It takes a function that produces the output of the testcase and calls it repeatedly until it matches the expected output (for at most 1 second).

Returns the last value returned by f (which can be directly returned from the function passed to RunTest).

If --rewrite is used, just sleeps for 100ms.

func (*TestData) RetryFor added in v1.0.3

func (td *TestData) RetryFor(tb testing.TB, d time.Duration, f func() string) string

RetryFor is like Retry but with a custom timeout.

func (*TestData) ScanArgs

func (td *TestData) ScanArgs(t testing.TB, key string, dests ...interface{})

ScanArgs looks up the first CmdArg matching the given key and scans it into the given destinations in order. If the arg does not exist, the number of destinations does not match that of the arguments, or a destination can not be populated from its matching value, a fatal error results. If the arg exists multiple times, the first occurrence is parsed. For example, for a TestData originating from

cmd arg1=50 arg2=yoruba arg3=(50, 50, 50)

the following would be valid:

var i1, i2, i3, i4 int
var s string
td.ScanArgs(t, "arg1", &i1)
td.ScanArgs(t, "arg2", &s)
td.ScanArgs(t, "arg3", &i2, &i3, &i4)

func (*TestData) String added in v1.0.3

func (td *TestData) String() string

String renders the entire testcase. The string ends in a newline.

Directories

Path Synopsis
Package diagram contains tools that can be used to generate ascii diagrams for datadriven tests.
Package diagram contains tools that can be used to generate ascii diagrams for datadriven tests.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL