Skip to content

Commit

Permalink
finish implementation of json matches by with diff view and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Aaron Kaswen-Wilk committed Dec 30, 2024
1 parent 34cdc2b commit f85dd19
Show file tree
Hide file tree
Showing 4 changed files with 241 additions and 168 deletions.
67 changes: 49 additions & 18 deletions assert/assertions.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"github.com/pmezard/go-difflib/difflib"

// Wrapper around gopkg.in/yaml.v3
"github.com/stretchr/testify/assert/internal/jsonmatch"
"github.com/stretchr/testify/assert/yaml"
)

Expand Down Expand Up @@ -210,7 +211,6 @@ the problem actually occurred in calling code.*/
// of each stack frame leading from the current test to the assert call that
// failed.
func CallerInfo() []string {

var pc uintptr
var ok bool
var file string
Expand Down Expand Up @@ -475,7 +475,6 @@ func Equal(t TestingT, expected, actual interface{}, msgAndArgs ...interface{})
}

return true

}

// validateEqualArgs checks whether provided arguments can be safely used in the
Expand Down Expand Up @@ -530,7 +529,7 @@ func NotSame(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}

same, ok := samePointers(expected, actual)
if !ok {
//fails when the arguments are not pointers
// fails when the arguments are not pointers
return !(Fail(t, "Both arguments must be pointers", msgAndArgs...))
}

Expand All @@ -549,7 +548,7 @@ func NotSame(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}
func samePointers(first, second interface{}) (same bool, ok bool) {
firstPtr, secondPtr := reflect.ValueOf(first), reflect.ValueOf(second)
if firstPtr.Kind() != reflect.Ptr || secondPtr.Kind() != reflect.Ptr {
return false, false //not both are pointers
return false, false // not both are pointers
}

firstType, secondType := reflect.TypeOf(first), reflect.TypeOf(second)
Expand Down Expand Up @@ -610,7 +609,6 @@ func EqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interfa
}

return true

}

// EqualExportedValues asserts that the types of two objects are equal and their public
Expand Down Expand Up @@ -665,7 +663,6 @@ func Exactly(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}
}

return Equal(t, expected, actual, msgAndArgs...)

}

// NotNil asserts that the specified object is not nil.
Expand Down Expand Up @@ -715,7 +712,6 @@ func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {

// isEmpty gets whether the specified object is considered empty or not.
func isEmpty(object interface{}) bool {

// get nil case out of the way
if object == nil {
return true
Expand Down Expand Up @@ -756,7 +752,6 @@ func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
}

return pass

}

// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either
Expand All @@ -775,7 +770,6 @@ func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
}

return pass

}

// getLen tries to get the length of an object.
Expand Down Expand Up @@ -819,7 +813,6 @@ func True(t TestingT, value bool, msgAndArgs ...interface{}) bool {
}

return true

}

// False asserts that the specified value is false.
Expand All @@ -834,7 +827,6 @@ func False(t TestingT, value bool, msgAndArgs ...interface{}) bool {
}

return true

}

// NotEqual asserts that the specified values are NOT equal.
Expand All @@ -857,7 +849,6 @@ func NotEqual(t TestingT, expected, actual interface{}, msgAndArgs ...interface{
}

return true

}

// NotEqualValues asserts that two objects are not equal even when converted to the same type
Expand All @@ -880,7 +871,6 @@ func NotEqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...inte
// return (true, false) if element was not found.
// return (true, true) if element was found.
func containsElement(list interface{}, element interface{}) (ok, found bool) {

listValue := reflect.ValueOf(list)
listType := reflect.TypeOf(list)
if listType == nil {
Expand Down Expand Up @@ -915,7 +905,6 @@ func containsElement(list interface{}, element interface{}) (ok, found bool) {
}
}
return true, false

}

// Contains asserts that the specified string, list(array, slice...) or map contains the
Expand All @@ -938,7 +927,6 @@ func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bo
}

return true

}

// NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the
Expand All @@ -961,7 +949,6 @@ func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{})
}

return true

}

// Subset asserts that the specified list(array, slice...) or map contains all
Expand Down Expand Up @@ -1667,7 +1654,6 @@ func matchRegexp(rx interface{}, str interface{}) bool {
default:
return r.MatchString(fmt.Sprint(v))
}

}

// Regexp asserts that a specified regexp matches a string.
Expand Down Expand Up @@ -1703,7 +1689,6 @@ func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interf
}

return !match

}

// Zero asserts that i is the zero value for its type.
Expand Down Expand Up @@ -1821,6 +1806,52 @@ func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{
return Equal(t, expectedJSONAsInterface, actualJSONAsInterface, msgAndArgs...)
}

type ValueMatchers = jsonmatch.Matchers

// JSONMatchesBy asserts that two JSON strings are equivalent using one or more custom JSON matchers.
// the value passed into the matcher will be on of the following types:
// - string
// - float64
// - bool
// - nil
// - []interface{}
// - map[string]interface{}
//
// Can use nil for matchers to test equality of pure json.
//
// Note: in cases where expected and actual are not equal, the value for the matcher will be displayed as unequal,
// regardless of the output of the matcher function. This should be fixed in a future release.
//
// assert.JSONMatchesBy(t, `{ "foo": "$NOT_EMPTY" }`, `{ "foo": "baz" }`, assert.ValueMatchers{
// "$NOT_EMPTY": func(v interface) bool { return v != "" },
// })
func JSONMatchesBy(t TestingT, expected string, actual string, matchers ValueMatchers, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}

var expectedJSONAsInterface, actualJSONAsInterface interface{}

if err := json.Unmarshal([]byte(expected), &expectedJSONAsInterface); err != nil {
return Fail(t, fmt.Sprintf("Expected value ('%s') is not valid json.\nJSON parsing error: '%s'", expected, err.Error()), msgAndArgs...)
}

if err := json.Unmarshal([]byte(actual), &actualJSONAsInterface); err != nil {
return Fail(t, fmt.Sprintf("Input ('%s') needs to be valid json.\nJSON parsing error: '%s'", actual, err.Error()), msgAndArgs...)
}

if !jsonmatch.MatchesBy(expectedJSONAsInterface, actualJSONAsInterface, matchers) {
diff := diff(expectedJSONAsInterface, actualJSONAsInterface)
expected, actual = formatUnequalValues(expectedJSONAsInterface, actualJSONAsInterface)
return Fail(t, fmt.Sprintf("Not equal: \n"+
"expected: %s\n"+
"actual : %s%s", expectedJSONAsInterface, actualJSONAsInterface, diff), msgAndArgs...)

}

return true
}

// YAMLEq asserts that two YAML strings are equivalent.
func YAMLEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
Expand Down
Loading

0 comments on commit f85dd19

Please sign in to comment.