Skip to content

Commit

Permalink
Merge branch 'master' into generate-better-comments-for-require-package
Browse files Browse the repository at this point in the history
  • Loading branch information
brackendawson authored Oct 23, 2024
2 parents 3b2754b + a012e45 commit f9ccf14
Show file tree
Hide file tree
Showing 14 changed files with 250 additions and 36 deletions.
1 change: 1 addition & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ jobs:
- "1.19"
- "1.20"
- "1.21"
- "1.22"
steps:
- uses: actions/checkout@v4
- name: Setup Go
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,8 @@ You can use the [mockery tool](https://vektra.github.io/mockery/latest/) to auto

[`suite`](https://pkg.go.dev/github.com/stretchr/testify/suite "API documentation") package
-----------------------------------------------------------------------------------------
> [!WARNING]
> The suite package does not support parallel tests. See [#934](https://github.com/stretchr/testify/issues/934).
The `suite` package provides functionality that you might be used to from more common object-oriented languages. With it, you can build a testing suite as a struct, build setup/teardown methods and testing methods on your struct, and run them with 'go test' as per normal.

Expand Down
9 changes: 9 additions & 0 deletions assert/assertion_format.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 18 additions & 0 deletions assert/assertion_forward.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 18 additions & 0 deletions assert/assertions.go
Original file line number Diff line number Diff line change
Expand Up @@ -2149,6 +2149,24 @@ func ErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interface{
), msgAndArgs...)
}

// NotErrorAs asserts that none of the errors in err's chain matches target,
// but if so, sets target to that error value.
func NotErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if !errors.As(err, target) {
return true
}

chain := buildErrorChainString(err)

return Fail(t, fmt.Sprintf("Target error should not be in err chain:\n"+
"found: %q\n"+
"in chain: %s", target, chain,
), msgAndArgs...)
}

func buildErrorChainString(err error) string {
if err == nil {
return ""
Expand Down
34 changes: 31 additions & 3 deletions assert/assertions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1327,7 +1327,7 @@ func TestNotElementsMatch(t *testing.T) {
actual interface{}
result bool
}{
// not mathing
// not matching
{[]int{1}, []int{}, true},
{[]int{}, []int{2}, true},
{[]int{1}, []int{2}, true},
Expand Down Expand Up @@ -3269,7 +3269,6 @@ func TestNotErrorIs(t *testing.T) {
}

func TestErrorAs(t *testing.T) {
mockT := new(testing.T)
tests := []struct {
err error
result bool
Expand All @@ -3282,9 +3281,38 @@ func TestErrorAs(t *testing.T) {
tt := tt
var target *customError
t.Run(fmt.Sprintf("ErrorAs(%#v,%#v)", tt.err, target), func(t *testing.T) {
mockT := new(testing.T)
res := ErrorAs(mockT, tt.err, &target)
if res != tt.result {
t.Errorf("ErrorAs(%#v,%#v) should return %t)", tt.err, target, tt.result)
t.Errorf("ErrorAs(%#v,%#v) should return %t", tt.err, target, tt.result)
}
if res == mockT.Failed() {
t.Errorf("The test result (%t) should be reflected in the testing.T type (%t)", res, !mockT.Failed())
}
})
}
}

func TestNotErrorAs(t *testing.T) {
tests := []struct {
err error
result bool
}{
{fmt.Errorf("wrap: %w", &customError{}), false},
{io.EOF, true},
{nil, true},
}
for _, tt := range tests {
tt := tt
var target *customError
t.Run(fmt.Sprintf("NotErrorAs(%#v,%#v)", tt.err, target), func(t *testing.T) {
mockT := new(testing.T)
res := NotErrorAs(mockT, tt.err, &target)
if res != tt.result {
t.Errorf("NotErrorAs(%#v,%#v) should not return %t", tt.err, target, tt.result)
}
if res == mockT.Failed() {
t.Errorf("The test result (%t) should be reflected in the testing.T type (%t)", res, !mockT.Failed())
}
})
}
Expand Down
2 changes: 1 addition & 1 deletion assert/yaml/yaml_custom.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
// import assertYaml "github.com/stretchr/testify/assert/yaml"
//
// func init() {
// assertYaml.Unmarshall = func (in []byte, out interface{}) error {
// assertYaml.Unmarshal = func (in []byte, out interface{}) error {
// // ...
// return nil
// }
Expand Down
2 changes: 1 addition & 1 deletion assert/yaml/yaml_default.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
//
// This package is just an indirection that allows the builder to override the
// indirection with an alternative implementation of this package that uses
// another implemantation of YAML deserialization. This allows to not either not
// another implementation of YAML deserialization. This allows to not either not
// use YAML deserialization at all, or to use another implementation than
// [gopkg.in/yaml.v3] (for example for license compatibility reasons, see [PR #1120]).
//
Expand Down
60 changes: 36 additions & 24 deletions mock/mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,12 @@ type Call struct {
requires []*Call
}

func newCall(parent *Mock, methodName string, callerInfo []string, methodArguments ...interface{}) *Call {
func newCall(parent *Mock, methodName string, callerInfo []string, methodArguments Arguments, returnArguments Arguments) *Call {
return &Call{
Parent: parent,
Method: methodName,
Arguments: methodArguments,
ReturnArguments: make([]interface{}, 0),
ReturnArguments: returnArguments,
callerInfo: callerInfo,
Repeatability: 0,
WaitFor: nil,
Expand Down Expand Up @@ -256,7 +256,7 @@ func (c *Call) Unset() *Call {
// calls have been called as expected. The referenced calls may be from the
// same mock instance and/or other mock instances.
//
// Mock.On("Do").Return(nil).Notbefore(
// Mock.On("Do").Return(nil).NotBefore(
// Mock.On("Init").Return(nil)
// )
func (c *Call) NotBefore(calls ...*Call) *Call {
Expand All @@ -273,6 +273,20 @@ func (c *Call) NotBefore(calls ...*Call) *Call {
return c
}

// InOrder defines the order in which the calls should be made
//
// For example:
//
// InOrder(
// Mock.On("init").Return(nil),
// Mock.On("Do").Return(nil),
// )
func InOrder(calls ...*Call) {
for i := 1; i < len(calls); i++ {
calls[i].NotBefore(calls[i-1])
}
}

// Mock is the workhorse used to track activity on another object.
// For an example of its usage, refer to the "Example Usage" section at the top
// of this document.
Expand Down Expand Up @@ -351,7 +365,8 @@ func (m *Mock) On(methodName string, arguments ...interface{}) *Call {

m.mutex.Lock()
defer m.mutex.Unlock()
c := newCall(m, methodName, assert.CallerInfo(), arguments...)

c := newCall(m, methodName, assert.CallerInfo(), arguments, make([]interface{}, 0))
m.ExpectedCalls = append(m.ExpectedCalls, c)
return c
}
Expand Down Expand Up @@ -491,11 +506,12 @@ func (m *Mock) MethodCalled(methodName string, arguments ...interface{}) Argumen
m.mutex.Unlock()

if closestCall != nil {
m.fail("\n\nmock: Unexpected Method Call\n-----------------------------\n\n%s\n\nThe closest call I have is: \n\n%s\n\n%s\nDiff: %s",
m.fail("\n\nmock: Unexpected Method Call\n-----------------------------\n\n%s\n\nThe closest call I have is: \n\n%s\n\n%s\nDiff: %s\nat: %s\n",
callString(methodName, arguments, true),
callString(methodName, closestCall.Arguments, true),
diffArguments(closestCall.Arguments, arguments),
strings.TrimSpace(mismatch),
assert.CallerInfo(),
)
} else {
m.fail("\nassert: mock: I don't know what to return because the method call was unexpected.\n\tEither do Mock.On(\"%s\").Return(...) first, or remove the %s() call.\n\tThis method was unexpected:\n\t\t%s\n\tat: %s", methodName, methodName, callString(methodName, arguments, true), assert.CallerInfo())
Expand Down Expand Up @@ -529,7 +545,7 @@ func (m *Mock) MethodCalled(methodName string, arguments ...interface{}) Argumen
call.totalCalls++

// add the call
m.Calls = append(m.Calls, *newCall(m, methodName, assert.CallerInfo(), arguments...))
m.Calls = append(m.Calls, *newCall(m, methodName, assert.CallerInfo(), arguments, call.ReturnArguments))
m.mutex.Unlock()

// block if specified
Expand Down Expand Up @@ -811,32 +827,31 @@ func IsType(t interface{}) *IsTypeArgument {
return &IsTypeArgument{t: reflect.TypeOf(t)}
}

// FunctionalOptionsArgument is a struct that contains the type and value of an functional option argument
// for use when type checking.
// FunctionalOptionsArgument contains a list of functional options arguments
// expected for use when matching a list of arguments.
type FunctionalOptionsArgument struct {
value interface{}
values []interface{}
}

// String returns the string representation of FunctionalOptionsArgument
func (f *FunctionalOptionsArgument) String() string {
var name string
tValue := reflect.ValueOf(f.value)
if tValue.Len() > 0 {
name = "[]" + reflect.TypeOf(tValue.Index(0).Interface()).String()
if len(f.values) > 0 {
name = "[]" + reflect.TypeOf(f.values[0]).String()
}

return strings.Replace(fmt.Sprintf("%#v", f.value), "[]interface {}", name, 1)
return strings.Replace(fmt.Sprintf("%#v", f.values), "[]interface {}", name, 1)
}

// FunctionalOptions returns an [FunctionalOptionsArgument] object containing
// the expected functional-options to check for.
//
// For example:
//
// Assert(t, FunctionalOptions(foo.Opt1("strValue"), foo.Opt2(613)))
func FunctionalOptions(value ...interface{}) *FunctionalOptionsArgument {
// args.Assert(t, FunctionalOptions(foo.Opt1("strValue"), foo.Opt2(613)))
func FunctionalOptions(values ...interface{}) *FunctionalOptionsArgument {
return &FunctionalOptionsArgument{
value: value,
values: values,
}
}

Expand Down Expand Up @@ -990,20 +1005,17 @@ func (args Arguments) Diff(objects []interface{}) (string, int) {
output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, expected.t.Name(), actualT.Name(), actualFmt)
}
case *FunctionalOptionsArgument:
t := expected.value

var name string
tValue := reflect.ValueOf(t)
if tValue.Len() > 0 {
name = "[]" + reflect.TypeOf(tValue.Index(0).Interface()).String()
if len(expected.values) > 0 {
name = "[]" + reflect.TypeOf(expected.values[0]).String()
}

tName := reflect.TypeOf(t).Name()
if name != reflect.TypeOf(actual).String() && tValue.Len() != 0 {
const tName = "[]interface{}"
if name != reflect.TypeOf(actual).String() && len(expected.values) != 0 {
differences++
output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, tName, reflect.TypeOf(actual).Name(), actualFmt)
} else {
if ef, af := assertOpts(t, actual); ef == "" && af == "" {
if ef, af := assertOpts(expected.values, actual); ef == "" && af == "" {
// match
output = fmt.Sprintf("%s\t%d: PASS: %s == %s\n", output, i, tName, tName)
} else {
Expand Down
Loading

0 comments on commit f9ccf14

Please sign in to comment.