diff --git a/equal.go b/equal.go index 1853c15..0b3c2c5 100644 --- a/equal.go +++ b/equal.go @@ -122,7 +122,7 @@ func (c Comparer) varCollected(s string, v interface{}) bool { return false } -func (c Comparer) filterDeltas(deltas []gojsondiff.Delta) []gojsondiff.Delta { +func (c Comparer) filterDeltas(deltas []gojsondiff.Delta, ignoreAdded bool) []gojsondiff.Delta { result := make([]gojsondiff.Delta, 0, len(deltas)) for _, delta := range deltas { @@ -142,19 +142,24 @@ func (c Comparer) filterDeltas(deltas []gojsondiff.Delta) []gojsondiff.Delta { } } case *gojsondiff.Object: - v.Deltas = c.filterDeltas(v.Deltas) + v.Deltas = c.filterDeltas(v.Deltas, ignoreAdded) if len(v.Deltas) == 0 { continue } delta = v case *gojsondiff.Array: - v.Deltas = c.filterDeltas(v.Deltas) + v.Deltas = c.filterDeltas(v.Deltas, ignoreAdded) if len(v.Deltas) == 0 { continue } delta = v + + case *gojsondiff.Added: + if ignoreAdded { + continue + } } result = append(result, delta) @@ -237,6 +242,10 @@ func (c Comparer) FailNotEqualMarshal(expected []byte, actualValue interface{}) // FailNotEqual returns error if JSON payloads are different, nil otherwise. func (c Comparer) FailNotEqual(expected, actual []byte) error { + return c.fail(expected, actual, false) +} + +func (c Comparer) fail(expected, actual []byte, ignoreAdded bool) error { var expDecoded, actDecoded interface{} expected, err := c.filterExpected(expected) @@ -277,7 +286,7 @@ func (c Comparer) FailNotEqual(expected, actual []byte) error { return nil } - diffValue = &diff{deltas: c.filterDeltas(diffValue.Deltas())} + diffValue = &diff{deltas: c.filterDeltas(diffValue.Deltas(), ignoreAdded)} if !diffValue.Modified() { return nil } diff --git a/matches.go b/matches.go new file mode 100644 index 0000000..16fc377 --- /dev/null +++ b/matches.go @@ -0,0 +1,92 @@ +package assertjson + +import ( + "strings" + + "github.com/stretchr/testify/assert" +) + +// Matches compares two JSON payloads. +// It ignores added fields in actual JSON payload. +func (c Comparer) Matches(t TestingT, expected, actual []byte, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + err := c.FailMismatch(expected, actual) + if err == nil { + return true + } + + msg := err.Error() + msg = strings.ToUpper(msg[0:1]) + msg[1:] + assert.Fail(t, msg, msgAndArgs...) + + return false +} + +// MatchesMarshal marshals actual JSON payload and compares it with expected payload. +// It ignores added fields in actual JSON payload. +func (c Comparer) MatchesMarshal(t TestingT, expected []byte, actualValue interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + actual, err := MarshalIndentCompact(actualValue, "", " ", 80) + assert.NoError(t, err, "failed to marshal actual value") + + if len(msgAndArgs) == 0 { + msgAndArgs = append(msgAndArgs, string(actual)) + } + + return c.Matches(t, expected, actual, msgAndArgs...) +} + +// FailMismatch returns error if expected JSON payload does not match actual JSON payload, nil otherwise. +// It ignores added fields in actual JSON payload. +func FailMismatch(expected, actual []byte) error { + return defaultComparer.FailMismatch(expected, actual) +} + +// FailMismatchMarshal returns error if expected JSON payload does not match marshaled actual value, nil otherwise. +// It ignores added fields in actual JSON payload. +func FailMismatchMarshal(expected []byte, actualValue interface{}) error { + return defaultComparer.FailMismatchMarshal(expected, actualValue) +} + +// FailMismatchMarshal returns error if expected JSON payload does not match marshaled actual value, nil otherwise. +// It ignores added fields in actual JSON payload. +func (c Comparer) FailMismatchMarshal(expected []byte, actualValue interface{}) error { + actual, err := MarshalIndentCompact(actualValue, "", " ", 80) + if err != nil { + return err + } + + return c.FailMismatch(expected, actual) +} + +// FailMismatch returns error if expected JSON payload does not match actual JSON payload, nil otherwise. +// It ignores added fields in actual JSON payload. +func (c Comparer) FailMismatch(expected, actual []byte) error { + return c.fail(expected, actual, true) +} + +// Matches compares two JSON documents ignoring string values "". +// It ignores added fields in actual JSON payload. +func Matches(t TestingT, expected, actual []byte, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + return defaultComparer.Matches(t, expected, actual, msgAndArgs...) +} + +// MatchesMarshal marshals actual value and compares two JSON documents ignoring string values "". +// It ignores added fields in actual JSON payload. +func MatchesMarshal(t TestingT, expected []byte, actualValue interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + return defaultComparer.MatchesMarshal(t, expected, actualValue, msgAndArgs...) +} diff --git a/matches_test.go b/matches_test.go new file mode 100644 index 0000000..44de3f8 --- /dev/null +++ b/matches_test.go @@ -0,0 +1,42 @@ +package assertjson_test + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/swaggest/assertjson" +) + +func TestFailMismatch(t *testing.T) { + act := json.RawMessage(`{"a": 1, "b": 2, "c": {"d": 1, "e": 2}}`) + exp := []byte(`{"a": 1, "c": {"d": 1}}`) + expFail := []byte(`{"a": 1, "c": {"d": 2}}`) + + assert.EqualError(t, assertjson.FailNotEqualMarshal(exp, act), `not equal: + { + "a": 1, + "c": { + "d": 1 ++ "e": 2 + } ++ "b": 2 + } +`) + + assert.NoError(t, assertjson.FailMismatchMarshal(exp, act)) + assert.NoError(t, assertjson.FailMismatch(exp, act)) + assert.EqualError(t, assertjson.FailMismatchMarshal(expFail, act), `not equal: + { + "a": 1, + "c": { +- "d": 2 ++ "d": 1 + } + } +`) + + assert.False(t, assertjson.MatchesMarshal(testingT(func(format string, args ...interface{}) {}), expFail, act)) + assertjson.MatchesMarshal(t, exp, act) + assertjson.Matches(t, exp, act) +}