Documentation ¶
Overview ¶
Package testhelper contains helper functions for doing things you might want to do in lots of your tests. For instance checking that an error value or a panic status was as expected.
The functions where a 't *testing.T' is passed will mark themselves as helpers by calling t.Helper(). These will also report the error themselves.
The testID parameter that many of the helpers take should be a string which can identify the instance of the test that is being run. For instance, if you have a slice of test cases you might want this string to include the index of the test and possibly some descriptive name. It might be created and used as follows:
testcases := []struct{ name string ... }{ { name: "whatever", }, } ... for i, tc := range testcases { tcID := fmt.Sprintf("test %d: %s", i, tc.name) ... testhelper.SomeFunc(t, tcID, ...) ... }
Alternatively you can construct the testcase struct with an embedded testhelper.ID member. Then the testcase ID can be initialised with the MkID func. The ID string can then be created with the IDStr() func defined on the testhelper.ID as follows:
testcases := []struct{ testhelper.ID ... }{ { ID: testhelper.MkID("whatever"), }, } ... for _, tc := range testcases { tcID := tc.IDStr() ... testhelper.SomeFunc(t, tcID, ...) ... }
This way of constructing a test case struct has the advantage that the constructed ID string gives more information about where the test was constructed and several of the testhelper functions take a testhelper.ID (or an interface which it satisfies).
The problem which the testhelper.ID solves is that reporting just the index number of a test (as in the previous example) imposes work on the tester such as counting tests to reach the failing test case. Using testhelper.ID gives you the filename and line number where the ID was instantiated.
There are additional mixin structs beside ID which allow you to record such things as whether an error is expected and if so what the error string should contain.
Index ¶
- Constants
- Variables
- func CheckAgainstGoldenFile(t *testing.T, testID string, val []byte, gfName string, updGF bool) booldeprecated
- func CheckError(t *testing.T, testID string, err error, expected bool, shouldContain []string) bool
- func CheckExpErr(t *testing.T, err error, tce TestCaseWithErr) bool
- func CheckExpErrWithID(t *testing.T, testID string, err error, te TestErr) bool
- func CheckExpPanic(t *testing.T, panicked bool, panicVal any, tp TestCaseWithPanic) bool
- func CheckExpPanicError(t *testing.T, panicked bool, panicVal any, tp TestCaseWithPanic) bool
- func CheckExpPanicErrorWithStack(t *testing.T, panicked bool, panicVal any, tp TestCaseWithPanic, ...) bool
- func CheckExpPanicWithStack(t *testing.T, panicked bool, panicVal any, tp TestCaseWithPanic, ...) bool
- func DiffBool(t *testing.T, id, name string, act, exp bool) bool
- func DiffErr(t *testing.T, id, name string, act, exp error) bool
- func DiffFloat[T constraints.Float](t *testing.T, id, name string, act, exp, epsilon T) bool
- func DiffFloatSlice[F constraints.Float](t *testing.T, id, name string, act, exp []F, epsilon F) bool
- func DiffInt[T constraints.Integer](t *testing.T, id, name string, act, exp T) bool
- func DiffSlice[C comparable](t *testing.T, id, name string, act, exp []C) bool
- func DiffString[S ~string](t *testing.T, id, name string, act, exp S) bool
- func DiffStringSlice[S ~string](t *testing.T, id, name string, act, exp []S) bool
- func DiffStringer(t *testing.T, id, name string, actS, expS fmt.Stringer) bool
- func DiffTime(t *testing.T, id, name string, act, exp time.Time) bool
- func DiffVals(actVal, expVal any, ignore ...[]string) error
- func ErrSliceToStrSlice(es []error) []string
- func MakeTempDir(name string, perms os.FileMode) func()
- func MakeTempDirCopy(fromDir string) (string, func() error, error)
- func PanicCheckError(t *testing.T, testID string, panicked, panicExpected bool, panicVal any, ...) bool
- func PanicCheckErrorWithStack(t *testing.T, testID string, panicked, panicExpected bool, panicVal any, ...) bool
- func PanicCheckString(t *testing.T, testID string, panicked, panicExpected bool, panicVal any, ...) bool
- func PanicCheckStringWithStack(t *testing.T, testID string, panicked, panicExpected bool, panicVal any, ...) bool
- func PanicSafe(f func()) (panicked bool, panicVal any)
- func ReportUnexpectedPanic(t *testing.T, testID string, panicked bool, panicVal any, stackTrace []byte) bool
- func ShouldContain(t *testing.T, testID, desc, act string, exp []string) bool
- func StringSliceDiff(a, b []string) bool
- func TempChmod(name string, perms os.FileMode) func()
- type DiffValErr
- type EnvCache
- type EnvEntry
- type ExpErr
- type ExpPanic
- type FakeIO
- type GoldenFileCfg
- type ID
- type TestCase
- type TestCaseWithErr
- type TestCaseWithPanic
- type TestErr
- type TestPanic
Examples ¶
Constants ¶
const MaxReportedDiffs = 5
Variables ¶
var OrigStdout = os.Stdout
Functions ¶
func CheckAgainstGoldenFile
deprecated
func CheckAgainstGoldenFile(t *testing.T, testID string, val []byte, gfName string, updGF bool) bool
CheckAgainstGoldenFile confirms that the value given matches the contents of the golden file and returns true if it does, false otherwise. It will report any errors it finds including any problems reading from or writing to the golden file itself. If the updGF flag is set to true then the golden file will be updated with the supplied value. You can set this value through a command-line parameter to the test and then pass that to this function as follows
var upd = flag.Bool("upd-gf", false, "update the golden files") gfc := testhelper.GoldenFileCfg{ DirNames: []string{"testdata"}, Pfx: "values", Sfx: "txt", } ... testhelper.CheckAgainstGoldenFile(t, "my value test", val, gfc.PathName(t.Name()), *upd)
Then to update the golden files you would invoke the test command as follows
go test -upd-gf
Give the -v argument to go test to see what is being updated.
Deprecated: use the Check method on the GoldenFileCfg
func CheckError ¶
CheckError checks that the error is nil if it is not expected, that it is non-nil if it is expected and that it contains the expected content if it is expected and non-nil. It will return false if there is any problem with the error, true otherwise
func CheckExpErr ¶
func CheckExpErr(t *testing.T, err error, tce TestCaseWithErr) bool
CheckExpErr calls CheckError using the details from the test case to supply the parameters
func CheckExpErrWithID ¶
CheckExpErrWithID calls CheckError using the details from the TestErr to supply the parameters. The testID is supplied separately
func CheckExpPanic ¶
CheckExpPanic calls PanicCheckString using the details from the test case to supply the parameters
func CheckExpPanicError ¶
CheckExpPanicError calls PanicCheckError using the details from the test case to supply the parameters
func CheckExpPanicErrorWithStack ¶
func CheckExpPanicErrorWithStack(t *testing.T, panicked bool, panicVal any, tp TestCaseWithPanic, stackTrace []byte, ) bool
CheckExpPanicErrorWithStack calls PanicCheckErrorWithStack using the details from the test case to supply the parameters
func CheckExpPanicWithStack ¶
func CheckExpPanicWithStack( t *testing.T, panicked bool, panicVal any, tp TestCaseWithPanic, stackTrace []byte, ) bool
CheckExpPanicWithStack calls PanicCheckStringWithStack using the details from the test case to supply the parameters
func DiffBool ¶
DiffBool compares the actual against the expected value and reports an error if they differ
func DiffErr ¶
DiffErr compares the actual against the expected value and reports an error if they differ. Note that it compares the string representation and not the error type so there might be a mismatch.
func DiffFloat ¶
DiffFloat compares the actual against the expected value and reports an error if they differ by more than epsilon
func DiffFloatSlice ¶
func DiffFloatSlice[F constraints.Float](t *testing.T, id, name string, act, exp []F, epsilon F, ) bool
DiffFloat64Slice compares the actual against the expected value and reports an error if they differ. At most MaxReportedDiffs are reported.
func DiffInt ¶
DiffInt compares the actual against the expected value and reports an error if they differ
func DiffSlice ¶
func DiffSlice[C comparable](t *testing.T, id, name string, act, exp []C) bool
DiffSlice compares the actual against the expected value and reports an error if they differ. At most MaxReportedDiffs are reported.
func DiffString ¶
DiffString compares the actual against the expected value and reports an error if they differ
func DiffStringSlice ¶
DiffStringSlice compares the actual against the expected value and reports an error if they differ. At most MaxReportedDiffs are reported.
func DiffStringer ¶
DiffStringer compares the actual against the expected value and reports an error if they differ. It will report them as different if one is nil or has a nil value and the other isn't/doesn't or if they are both non-nil and the string values differ.
func DiffTime ¶
DiffTime compares the actual against the expected value and reports an error if they differ
func DiffVals ¶
DiffVals compares the actual and expected values and returns an error if they are different. This differs from the reflect package function DeepEqual in that the error shows which fields are different.
The ignore argument holds the names of fields to not compare, the slice represents a chain of names in nested structs. So, for instance an ignore value containing the pair of values ["a", "b"] means to not compare the field called "b" in the sub-struct called "a".
func ErrSliceToStrSlice ¶
ErrSliceToStrSlice creates a string slice from the supplied slice of errors - each string is the result of the Error func being called on the corresponding error
func MakeTempDir ¶
MakeTempDir creates a directory with the given name and permissions and returns a function that will delete the directory. This can be used as a defer func to tidy up after the tests are complete. This might be useful where you want to create an empty or unreadable directory for a test.
It will panic if the directory cannot be created or the permissions set.
Only the Permission bits of the perms are used, any other values are masked out before use.
func MakeTempDirCopy ¶ added in v2.3.0
MakeTempDirCopy creates a temporary directory and copies the contents of fromDir into it. It returns the name of the temporary directory, a function to clean up (remove the temp dir) and any error encountered. Only regular files and sub-directories can be copied. Any other types of file in the from-directory will cause an error.
func PanicCheckError ¶
func PanicCheckError(t *testing.T, testID string, panicked, panicExpected bool, panicVal any, shouldContain []string, ) bool
PanicCheckError tests the panic value (which should be an error) against the passed values. It will report an error if the panic status is unexpected.
func PanicCheckErrorWithStack ¶
func PanicCheckErrorWithStack(t *testing.T, testID string, panicked, panicExpected bool, panicVal any, shouldContain []string, stackTrace []byte, ) bool
PanicCheckErrorWithStack tests the panic value (which should be an error) against the passed values. A stack trace should also be passed which will be printed if the panic is not as expected and a panic was seen
func PanicCheckString ¶
func PanicCheckString(t *testing.T, testID string, panicked, panicExpected bool, panicVal any, shouldContain []string, ) bool
PanicCheckString tests the panic value (which should be a string) against the passed values. It will report an error if the panic status is unexpected.
func PanicCheckStringWithStack ¶
func PanicCheckStringWithStack(t *testing.T, testID string, panicked, panicExpected bool, panicVal any, shouldContain []string, stackTrace []byte, ) bool
PanicCheckStringWithStack tests the panic value (which should be a string) against the passed values. A stack trace should also be passed which will be printed if the panic is not as expected and a panic was seen
func PanicSafe ¶
PanicSafe will call the passed func, check if it has panicked and return true and the recovered panic value if it has and false and nil otherwise. You can call it passing a closure that does the work you are testing or any func matching the signature. It is intended to reduce the volume of boilerplate code you need.
Example ¶
ExamplePanicSafe demonstrates how the PanicSafe function may be used
package main import ( "fmt" "github.com/nickwells/testhelper.mod/v2/testhelper" ) func main() { panicked, panicVal := testhelper.PanicSafe(func() { panic("As expected") }) fmt.Println("Panicked:", panicked) fmt.Println("PanicVal:", panicVal) }
Output: Panicked: true PanicVal: As expected
func ReportUnexpectedPanic ¶
func ReportUnexpectedPanic(t *testing.T, testID string, panicked bool, panicVal any, stackTrace []byte, ) bool
ReportUnexpectedPanic will check if panicked is true and will report the unexpected panic if true. it returns the panicked value
func ShouldContain ¶
ShouldContain checks that the string 'act' contains all of the strings in the 'exp' argument and reports an error if it does not. The desc parameter is used to describe the string being checked. It returns true if a problem was found, false otherwise.
func StringSliceDiff ¶
StringSliceDiff will compare two slices of strings for equality. If they are of different lengths they are taken to be different. A nil slice and an empty slice are taken to be the same.
func TempChmod ¶
TempChmod will change the FileMode of the named file and return a function that will restore the original perms.
It will panic if the permission bits can't be set or the original values obtained.
Only the Permission bits of the perms are used, any other values are masked out before use.
Types ¶
type DiffValErr ¶
type DiffValErr struct {
// contains filtered or unexported fields
}
DiffValErr holds an error reflecting the difference between two interface values. It is the type of error returned by the DiffVals func
func (DiffValErr) Error ¶
func (dve DiffValErr) Error() string
Error returns a string expressing the DiffValErr
type EnvCache ¶ added in v2.1.0
type EnvCache struct {
Stack []EnvEntry
}
EnvCache maintains a stack of EnvEntry's. It records the previous value of each environment variable which has been set by the Setenv method. This allows the values to be reset to their original values by the ResetEnv method.
func (*EnvCache) ResetEnv ¶ added in v2.1.0
func (ec *EnvCache) ResetEnv()
ResetEnv resets the environment to its state prior to the modifications made through the use of the Setenv method. It clears the stack after the environment has been restored. Note that the environment is not restored exactly as it was; variables which didn't previously exist at all will afterwards exist but with an empty value.
type ExpErr ¶
ExpErr records common details about error expectations for a test case. It is intended that this should be embedded in a test case structure, which will also have an ID structure embedded. The resulting test case can then be passed to the CheckExpErr func. It is similar to the ExpPanic structure in form and intended use.
Example ¶
package main import ( "github.com/nickwells/testhelper.mod/v2/testhelper" ) func main() { testCases := []struct { testhelper.ID testhelper.ExpErr v int }{ { ID: testhelper.MkID("No error expected"), v: 1, }, { ID: testhelper.MkID("error expected"), ExpErr: testhelper.MkExpErr("part1", "another part"), v: 99, }, } _ = testCases // this is just to ensure that the testCases var is used // for _, tc := range testCases { // // we can then run the test, collect any error returned and check // that it is as expected. With the ExpErr member left at its default // value no error is expecteed. If it has a value the error is // expected and it should contain all the supplied strings. // // With the testcase structure having an ID and an ExpErr you can // call // // testhelper.CheckExpErr(t, err, tc) // // just passing the testing.T pointer (t), the error (err) and the // testcase (tc). This will report any missing or unexpected errors // or errors that have unexpected values. It wil return false if // there are any problems // } }
Output:
func MkExpErr ¶
MkExpErr is a constructor for the ExpErr struct. The Expected flag is always set to true and the slice of strings that the error should contain is set to the slice of strings passed. For an ExpErr where the error is not expected just leave it in its default state.
func (ExpErr) ErrExpected ¶
ErrExpected returns true or false according to the value of the Expected field
func (ExpErr) ErrShldCont ¶
ErrShldCont returns the value of the ShouldContain field
type ExpPanic ¶
ExpPanic records common details about panic expectations for a test case. It is intended that this should be embedded in a test case structure, which will also have an ID structure embedded. The resulting test case can then be passed to the CheckExpPanic func. It is similar to the ExpErr structure in form and intended use.
Note that this expects any panic value to be a string and it will report an error if that is not the case.
Example ¶
package main import ( "github.com/nickwells/testhelper.mod/v2/testhelper" ) func main() { testCases := []struct { testhelper.ID testhelper.ExpPanic v int }{ { ID: testhelper.MkID("No panic expected"), v: 1, }, { ID: testhelper.MkID("panic expected"), ExpPanic: testhelper.MkExpPanic("part1", "another part"), v: 2, }, } _ = testCases // this is just to ensure that the testCases var is used // for _, tc := range testCases { // Use the testhelper.PanicSafe func // // This func calls the code to be tested and recovers from any panics. // It returns true if a panic was seen and an interface value // containing the panic value. We then collect these returned values // and check that they are as expected. With the ExpPanic member left // at its default value no panic is expecteed. If it has a value the // panic is expected and it should contain all the supplied strings. // // With the testcase structure having an ID and an ExpPanic you can // call // // testhelper.CheckExpPanic(t, panicked, panicVal, tc) // // just passing the testing.T pointer (t), the boolean indicating // whether a panic was seen , the panic value and the testcase // (tc). This will report any missing or unexpected panics or panics // that have unexpected values. It wil return true if there are any // problems. There is an alternative function that can be called // which allows you to pass a stack trace as a final parameter; this // is useful if you get an unexpected panic and want to find out // where it came from // } }
Output:
func MkExpPanic ¶
MkExpPanic is a constructor for the ExpPanic struct. The Expected flag is always set to true and the slice of strings that the panic should contain is set to the slice of strings passed. For an ExpPanic where a panic is not expected just leave it in its default state.
func (ExpPanic) PanicExpected ¶
PanicExpected returns true or false according to the value of the PanicExpected field
func (ExpPanic) PanicShldCont ¶
PanicShldCont returns the value of the ShouldContain field
type FakeIO ¶ added in v2.2.0
FakeIO holds the details needed to redirect and restore Stdin, Stdout and Stderr for the duration of a test. Create this just before the test with a NewStdio... func and after the test is complete restore the original settings by calling the Done method which will also return the contents of anything written to stdout and stderr.
func NewStdioFromString ¶ added in v2.2.0
NewStdioFromString will create a Stdio object which will provide access to the contents of anything written to stdout or stderr. After this has been called any code reading from stdin will get the contents of the passed string. Any output to stdout or stderr will be captured
type GoldenFileCfg ¶
type GoldenFileCfg struct { DirNames []string Pfx string Sfx string UpdFlagName string KeepBadResultsFlagName string // contains filtered or unexported fields }
GoldenFileCfg holds common configuration details for a collection of golden files. It helps with consistent naming of golden files without having to repeat common parts throughout the code.
A golden file is a file that holds expected output (typically lengthy) that can be compared as part of a test. It avoids the need to have a long string in the body of a test.
DirNames is a slice of strings holding the parts of the directory path to the file Pfx is an optional prefix - leave it as an empty string to exclude it Sfx is an optional suffix - as for the prefix UpdFlagName is the name of a flag that will set a bool used to decide whether or not to update the golden file. If it is not set then it is ignored. If you have set this then you should also call the AddUpdateFlag method (typically in an init() function) and then use the Check method to compare with the file KeepBadResultsFlagName is the name of a flag that will set a bool used to decide whether or not to keep bad results. If it is not set then it is ignored. If you have set this then you should also call the AddKeepBadResultsFlag method (typically in an init() function) and then use the Check method to compare with the file
func (*GoldenFileCfg) AddKeepBadResultsFlag ¶
func (gfc *GoldenFileCfg) AddKeepBadResultsFlag()
AddKeepBadResultsFlag adds a new flag to the standard flag package. The flag is used to control whether or not to keep the bad results in a file. The name of the file will be the name of the Golden file with ".badResults" as a suffix. These files can then be compared with the Golden files do see what changes have been made . This can then be set on the command line when testing and looked up by the GoldenFileCfg.Check method. The Check method will report the flag name to use if any is available.
func (*GoldenFileCfg) AddUpdateFlag ¶
func (gfc *GoldenFileCfg) AddUpdateFlag()
AddUpdateFlag adds a new flag to the standard flag package. The flag is used to control whether or not to update the Golden files with the new values rather than reporting differences as test errors. If there is already a Golden file present then this will be preserved in a file with the same name as the Golden file but with ".orig" as a suffix. This can then be set on the command line when testing and looked up by the GoldenFileCfg.Check method. The Check method will report the flag name to use if any is available.
func (GoldenFileCfg) Check ¶
Check confirms that the value given matches the contents of the golden file and returns true if it does, false otherwise. It will report any errors it finds including any problems reading from or writing to the golden file itself.
If UpdFlagName is not empty and the AddUpdateFlag method has been called (typically in an init() function) then the corresponding flag value will be looked up and if the flag is set to true the golden file will be updated with the supplied value. You can set this value through a command-line parameter to the test and then pass that to this function as follows:
gfc := testhelper.GoldenFileCfg{ DirNames: []string{"testdata"}, Pfx: "values", Sfx: "txt", UpdFlagName: "upd-gf", } func init() { gfc.AddUpdateFlag() } ... gfc.Check(t, "my value test", t.Name(), val)
Then to update the golden files you would invoke the test command as follows:
go test -upd-gf
Similarly with the KeepBadResultsFlag.
Give the -v argument to go test to see what is being updated.
An advantage of using this method (over using the testhelper.CheckAgainstGoldenFile function) is that this will show the name of the flag to use in order to update the files. You save the hassle of scanning the code to find out what you called the flag.
func (GoldenFileCfg) PathName ¶
func (gfc GoldenFileCfg) PathName(name string) string
PathName will return the name of a golden file. It applies the directory names and any prefix or suffix to the supplied string to give a well-formed name using the appropriate filepath separators for the operating system. A suggested name to pass to this method might be the name of the current test as given by the Name() method on testing.T.
Note that any supplied name is "cleaned" by removing any part prior to an embedded filepath.Separator.
type ID ¶
ID holds common identifying information about a test. Several of the testhelper functions take an ID (or an interface which it satisfies) which can simplify the tests. This is often combined with other testhelper mixin structs to record standard details for common tests.
func MkID ¶
MkID is a constructor for the ID type. It will record where it was called from and the resulting ID, when used to report an error, will give information on the location which should speed up locating the test setup for the failing test.
func (ID) IDStr ¶
IDStr returns an identifying string describing the test
Example ¶
package main import ( "fmt" "github.com/nickwells/testhelper.mod/v2/testhelper" ) func main() { testCases := []struct { testhelper.ID v1, v2 int }{ { ID: testhelper.MkID("my first example"), v1: 1, v2: 2, }, } for _, tc := range testCases { if tc.v1 != tc.v2 { // You could use the testhelper.DiffInt(...) func fmt.Println(tc.IDStr()) // in a real test this will be t.Error(...) } } }
Output: test: examples_test.go:15: my first example
func (ID) IDStrFullName ¶
IDStrFullName returns an identifying string describing the test, using the full pathname of the file where the MkID func was called rather than just the last part of the path. You might want to use this if your test cases are initialised in a more complex way and there is some ambiguity as to the location of a source file.
type TestCaseWithErr ¶
TestCaseWithErr combines the TestCase and TestErr interfaces
type TestCaseWithPanic ¶
TestCaseWithPanic combines the TestCase and TestPanic interfaces