From 408a133ee4f562eeb1d48564aecedea078093275 Mon Sep 17 00:00:00 2001 From: Roger Guldbrandsen Date: Mon, 3 Jun 2019 17:42:07 +0100 Subject: [PATCH 1/2] Ignore stackframes from this package in assertions This means that the line number at which a test assertion appeared will show up at the right line of code relative to what the user wrote, as opposed to where in this package it appeared. --- array.go | 3 +++ boolean.go | 3 +++ core.go | 12 ++++++++++++ exports.go | 3 +++ number.go | 3 +++ object.go | 3 +++ string.go | 3 +++ 7 files changed, 30 insertions(+) diff --git a/array.go b/array.go index e6561db..aada887 100644 --- a/array.go +++ b/array.go @@ -7,6 +7,9 @@ import ( ) func (a *Asserter) checkArray(path string, act, exp []interface{}) { + if t, ok := a.Printer.(tt); ok { + t.Helper() + } if len(act) != len(exp) { a.Printer.Errorf("length of arrays at '%s' were different. Expected array to be of length %d, but contained %d element(s)", path, len(exp), len(act)) serializedAct, serializedExp := serialize(act), serialize(exp) diff --git a/boolean.go b/boolean.go index 1705f59..5b01dbb 100644 --- a/boolean.go +++ b/boolean.go @@ -13,6 +13,9 @@ func extractBoolean(b string) (bool, error) { } func (a *Asserter) checkBoolean(path string, act, exp bool) { + if t, ok := a.Printer.(tt); ok { + t.Helper() + } if act != exp { a.Printer.Errorf("expected boolean at '%s' to be %v but was %v", path, exp, act) } diff --git a/core.go b/core.go index dc3d6fd..83a674f 100644 --- a/core.go +++ b/core.go @@ -8,6 +8,9 @@ import ( ) func (a *Asserter) pathassertf(path, act, exp string) { + if t, ok := a.Printer.(tt); ok { + t.Helper() + } if act == exp { return } @@ -102,3 +105,12 @@ func findType(j string) (jsonType, error) { } return jsonTypeUnknown, fmt.Errorf(`unable to identify JSON type of "%s"`, j) } + +// *testing.T has a Helper() func that allow testing tools like this package to +// ignore their own frames when calling Errorf on *testing.T instances. +// This interface is here to avoid breaking backwards compatibility in terms of +// the interface we expect in New. +type tt interface { + Printer + Helper() +} diff --git a/exports.go b/exports.go index f4b9460..6d0be2b 100644 --- a/exports.go +++ b/exports.go @@ -54,5 +54,8 @@ func New(p Printer) *Asserter { // You may use "<>" against any type of value. The only exception is null, which // will result in an assertion failure. func (a *Asserter) Assertf(actualJSON, expectedJSON string, fmtArgs ...interface{}) { + if t, ok := a.Printer.(tt); ok { + t.Helper() + } a.pathassertf("$", actualJSON, fmt.Sprintf(expectedJSON, fmtArgs...)) } diff --git a/number.go b/number.go index a382151..742e8ed 100644 --- a/number.go +++ b/number.go @@ -9,6 +9,9 @@ import ( const minDiff = 0.000001 func (a *Asserter) checkNumber(path string, act, exp float64) { + if t, ok := a.Printer.(tt); ok { + t.Helper() + } diff := math.Abs(act - exp) if diff > minDiff { a.Printer.Errorf("expected number at '%s' to be '%.7f' but was '%.7f'", path, exp, act) diff --git a/object.go b/object.go index 469d217..61bb74e 100644 --- a/object.go +++ b/object.go @@ -7,6 +7,9 @@ import ( ) func (a *Asserter) checkObject(path string, act, exp map[string]interface{}) { + if t, ok := a.Printer.(tt); ok { + t.Helper() + } if len(act) != len(exp) { a.Printer.Errorf("expected %d keys at '%s' but got %d keys", len(exp), path, len(act)) } diff --git a/string.go b/string.go index 82e91e9..c222934 100644 --- a/string.go +++ b/string.go @@ -7,6 +7,9 @@ import ( ) func (a *Asserter) checkString(path, act, exp string) { + if t, ok := a.Printer.(tt); ok { + t.Helper() + } if act != exp { if len(exp+act) < 50 { a.Printer.Errorf("expected string at '%s' to be '%s' but was '%s'", path, exp, act) From 7d503027a0fefa0ef81bfa81771df819e19ffba4 Mon Sep 17 00:00:00 2001 From: Roger Guldbrandsen Date: Mon, 3 Jun 2019 18:12:52 +0100 Subject: [PATCH 2/2] Test Helper() invocation count --- integration_test.go | 78 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/integration_test.go b/integration_test.go index 60291cb..20e9f03 100644 --- a/integration_test.go +++ b/integration_test.go @@ -117,6 +117,84 @@ but expected JSON was: } } +type testTT struct { + testPrinter + invokedCount int +} + +func (tt *testTT) Helper() { + tt.invokedCount++ +} + +func TestHelperGetsCalled(t *testing.T) { + testTable := []struct { + name string + in string + exp string + expCount int + }{ + { + name: "1 at the top level, 1 for core", + in: `null`, + exp: `""`, + expCount: 2, + }, + { + name: "1 at the top level, 1 for core, 1 for boolean", + in: `true`, + exp: `false`, + expCount: 3, + }, + { + name: "1 at the top level, 1 for core, 1 for string", + in: `"hello"`, + exp: `"henlo"`, + expCount: 3, + }, + { + name: "1 at the top level, 1 for core, 1 for number", + in: `1234`, + exp: `4321`, + expCount: 3, + }, + { + name: "1 at the top level, 2 for core, 1 for object, 1 for string", + in: `{"hello": "world"}`, + exp: `{"hello": "世界"}`, + expCount: 5, + }, + { + name: "1 at the top level, 2 for core, 2 for object, 2 for string", + in: `{"hello": {"name": "kinbiko"}}`, + exp: `{"hello": {"name": "他の人"}}`, + expCount: 7, + }, + { + name: "1 at the top level, 2 for core, 1 for array, 2 for string", + in: `["hello", "world"]`, + exp: `["hello", "世界"]`, + expCount: 6, + }, + { + name: "1 at the top level, 4 for core, 3 for array, 4 for string", + in: `[["hello", "world"], ["bye", "moon"]]`, + exp: `[["hello", "世界"], ["bye", "月"]]`, + expCount: 13, + }, + } + + for _, tc := range testTable { + t.Run(tc.name, func(st *testing.T) { + tt := &testTT{} + ja := jsonassert.New(tt) + ja.Assertf(tc.in, tc.exp) // assertions should never fail + if got := tt.invokedCount; got != tc.expCount { + st.Errorf("expected %d calls to Helper but got %d", tc.expCount, got) + } + }) + } +} + func setup() (*testPrinter, *jsonassert.Asserter) { tp := &testPrinter{} return tp, jsonassert.New(tp)