1/*Package assert provides assertions for comparing expected values to actual 2values. When an assertion fails a helpful error message is printed. 3 4Assert and Check 5 6Assert() and Check() both accept a Comparison, and fail the test when the 7comparison fails. The one difference is that Assert() will end the test execution 8immediately (using t.FailNow()) whereas Check() will fail the test (using t.Fail()), 9return the value of the comparison, then proceed with the rest of the test case. 10 11Example usage 12 13The example below shows assert used with some common types. 14 15 16 import ( 17 "testing" 18 19 "gotest.tools/assert" 20 is "gotest.tools/assert/cmp" 21 ) 22 23 func TestEverything(t *testing.T) { 24 // booleans 25 assert.Assert(t, ok) 26 assert.Assert(t, !missing) 27 28 // primitives 29 assert.Equal(t, count, 1) 30 assert.Equal(t, msg, "the message") 31 assert.Assert(t, total != 10) // NotEqual 32 33 // errors 34 assert.NilError(t, closer.Close()) 35 assert.Error(t, err, "the exact error message") 36 assert.ErrorContains(t, err, "includes this") 37 assert.ErrorType(t, err, os.IsNotExist) 38 39 // complex types 40 assert.DeepEqual(t, result, myStruct{Name: "title"}) 41 assert.Assert(t, is.Len(items, 3)) 42 assert.Assert(t, len(sequence) != 0) // NotEmpty 43 assert.Assert(t, is.Contains(mapping, "key")) 44 45 // pointers and interface 46 assert.Assert(t, is.Nil(ref)) 47 assert.Assert(t, ref != nil) // NotNil 48 } 49 50Comparisons 51 52Package https://godoc.org/gotest.tools/assert/cmp provides 53many common comparisons. Additional comparisons can be written to compare 54values in other ways. See the example Assert (CustomComparison). 55 56Automated migration from testify 57 58gty-migrate-from-testify is a binary which can update source code which uses 59testify assertions to use the assertions provided by this package. 60 61See http://bit.do/cmd-gty-migrate-from-testify. 62 63 64*/ 65package assert // import "gotest.tools/assert" 66 67import ( 68 "fmt" 69 "go/ast" 70 "go/token" 71 72 gocmp "github.com/google/go-cmp/cmp" 73 "gotest.tools/assert/cmp" 74 "gotest.tools/internal/format" 75 "gotest.tools/internal/source" 76) 77 78// BoolOrComparison can be a bool, or cmp.Comparison. See Assert() for usage. 79type BoolOrComparison interface{} 80 81// TestingT is the subset of testing.T used by the assert package. 82type TestingT interface { 83 FailNow() 84 Fail() 85 Log(args ...interface{}) 86} 87 88type helperT interface { 89 Helper() 90} 91 92const failureMessage = "assertion failed: " 93 94// nolint: gocyclo 95func assert( 96 t TestingT, 97 failer func(), 98 argSelector argSelector, 99 comparison BoolOrComparison, 100 msgAndArgs ...interface{}, 101) bool { 102 if ht, ok := t.(helperT); ok { 103 ht.Helper() 104 } 105 var success bool 106 switch check := comparison.(type) { 107 case bool: 108 if check { 109 return true 110 } 111 logFailureFromBool(t, msgAndArgs...) 112 113 // Undocumented legacy comparison without Result type 114 case func() (success bool, message string): 115 success = runCompareFunc(t, check, msgAndArgs...) 116 117 case nil: 118 return true 119 120 case error: 121 msg := "error is not nil: " 122 t.Log(format.WithCustomMessage(failureMessage+msg+check.Error(), msgAndArgs...)) 123 124 case cmp.Comparison: 125 success = runComparison(t, argSelector, check, msgAndArgs...) 126 127 case func() cmp.Result: 128 success = runComparison(t, argSelector, check, msgAndArgs...) 129 130 default: 131 t.Log(fmt.Sprintf("invalid Comparison: %v (%T)", check, check)) 132 } 133 134 if success { 135 return true 136 } 137 failer() 138 return false 139} 140 141func runCompareFunc( 142 t TestingT, 143 f func() (success bool, message string), 144 msgAndArgs ...interface{}, 145) bool { 146 if ht, ok := t.(helperT); ok { 147 ht.Helper() 148 } 149 if success, message := f(); !success { 150 t.Log(format.WithCustomMessage(failureMessage+message, msgAndArgs...)) 151 return false 152 } 153 return true 154} 155 156func logFailureFromBool(t TestingT, msgAndArgs ...interface{}) { 157 if ht, ok := t.(helperT); ok { 158 ht.Helper() 159 } 160 const stackIndex = 3 // Assert()/Check(), assert(), formatFailureFromBool() 161 const comparisonArgPos = 1 162 args, err := source.CallExprArgs(stackIndex) 163 if err != nil { 164 t.Log(err.Error()) 165 return 166 } 167 168 msg, err := boolFailureMessage(args[comparisonArgPos]) 169 if err != nil { 170 t.Log(err.Error()) 171 msg = "expression is false" 172 } 173 174 t.Log(format.WithCustomMessage(failureMessage+msg, msgAndArgs...)) 175} 176 177func boolFailureMessage(expr ast.Expr) (string, error) { 178 if binaryExpr, ok := expr.(*ast.BinaryExpr); ok && binaryExpr.Op == token.NEQ { 179 x, err := source.FormatNode(binaryExpr.X) 180 if err != nil { 181 return "", err 182 } 183 y, err := source.FormatNode(binaryExpr.Y) 184 if err != nil { 185 return "", err 186 } 187 return x + " is " + y, nil 188 } 189 190 if unaryExpr, ok := expr.(*ast.UnaryExpr); ok && unaryExpr.Op == token.NOT { 191 x, err := source.FormatNode(unaryExpr.X) 192 if err != nil { 193 return "", err 194 } 195 return x + " is true", nil 196 } 197 198 formatted, err := source.FormatNode(expr) 199 if err != nil { 200 return "", err 201 } 202 return "expression is false: " + formatted, nil 203} 204 205// Assert performs a comparison. If the comparison fails the test is marked as 206// failed, a failure message is logged, and execution is stopped immediately. 207// 208// The comparison argument may be one of three types: bool, cmp.Comparison or 209// error. 210// When called with a bool the failure message will contain the literal source 211// code of the expression. 212// When called with a cmp.Comparison the comparison is responsible for producing 213// a helpful failure message. 214// When called with an error a nil value is considered success. A non-nil error 215// is a failure, and Error() is used as the failure message. 216func Assert(t TestingT, comparison BoolOrComparison, msgAndArgs ...interface{}) { 217 if ht, ok := t.(helperT); ok { 218 ht.Helper() 219 } 220 assert(t, t.FailNow, argsFromComparisonCall, comparison, msgAndArgs...) 221} 222 223// Check performs a comparison. If the comparison fails the test is marked as 224// failed, a failure message is logged, and Check returns false. Otherwise returns 225// true. 226// 227// See Assert for details about the comparison arg and failure messages. 228func Check(t TestingT, comparison BoolOrComparison, msgAndArgs ...interface{}) bool { 229 if ht, ok := t.(helperT); ok { 230 ht.Helper() 231 } 232 return assert(t, t.Fail, argsFromComparisonCall, comparison, msgAndArgs...) 233} 234 235// NilError fails the test immediately if err is not nil. 236// This is equivalent to Assert(t, err) 237func NilError(t TestingT, err error, msgAndArgs ...interface{}) { 238 if ht, ok := t.(helperT); ok { 239 ht.Helper() 240 } 241 assert(t, t.FailNow, argsAfterT, err, msgAndArgs...) 242} 243 244// Equal uses the == operator to assert two values are equal and fails the test 245// if they are not equal. 246// 247// If the comparison fails Equal will use the variable names for x and y as part 248// of the failure message to identify the actual and expected values. 249// 250// If either x or y are a multi-line string the failure message will include a 251// unified diff of the two values. If the values only differ by whitespace 252// the unified diff will be augmented by replacing whitespace characters with 253// visible characters to identify the whitespace difference. 254// 255// This is equivalent to Assert(t, cmp.Equal(x, y)). 256func Equal(t TestingT, x, y interface{}, msgAndArgs ...interface{}) { 257 if ht, ok := t.(helperT); ok { 258 ht.Helper() 259 } 260 assert(t, t.FailNow, argsAfterT, cmp.Equal(x, y), msgAndArgs...) 261} 262 263// DeepEqual uses google/go-cmp (http://bit.do/go-cmp) to assert two values are 264// equal and fails the test if they are not equal. 265// 266// Package https://godoc.org/gotest.tools/assert/opt provides some additional 267// commonly used Options. 268// 269// This is equivalent to Assert(t, cmp.DeepEqual(x, y)). 270func DeepEqual(t TestingT, x, y interface{}, opts ...gocmp.Option) { 271 if ht, ok := t.(helperT); ok { 272 ht.Helper() 273 } 274 assert(t, t.FailNow, argsAfterT, cmp.DeepEqual(x, y, opts...)) 275} 276 277// Error fails the test if err is nil, or the error message is not the expected 278// message. 279// Equivalent to Assert(t, cmp.Error(err, message)). 280func Error(t TestingT, err error, message string, msgAndArgs ...interface{}) { 281 if ht, ok := t.(helperT); ok { 282 ht.Helper() 283 } 284 assert(t, t.FailNow, argsAfterT, cmp.Error(err, message), msgAndArgs...) 285} 286 287// ErrorContains fails the test if err is nil, or the error message does not 288// contain the expected substring. 289// Equivalent to Assert(t, cmp.ErrorContains(err, substring)). 290func ErrorContains(t TestingT, err error, substring string, msgAndArgs ...interface{}) { 291 if ht, ok := t.(helperT); ok { 292 ht.Helper() 293 } 294 assert(t, t.FailNow, argsAfterT, cmp.ErrorContains(err, substring), msgAndArgs...) 295} 296 297// ErrorType fails the test if err is nil, or err is not the expected type. 298// 299// Expected can be one of: 300// a func(error) bool which returns true if the error is the expected type, 301// an instance of (or a pointer to) a struct of the expected type, 302// a pointer to an interface the error is expected to implement, 303// a reflect.Type of the expected struct or interface. 304// 305// Equivalent to Assert(t, cmp.ErrorType(err, expected)). 306func ErrorType(t TestingT, err error, expected interface{}, msgAndArgs ...interface{}) { 307 if ht, ok := t.(helperT); ok { 308 ht.Helper() 309 } 310 assert(t, t.FailNow, argsAfterT, cmp.ErrorType(err, expected), msgAndArgs...) 311} 312