Repository: gavv/httpexpect
Pull request #223 (2023-01-05 08:13:18)

Decode methods
Signed-off-by: Rohith Raju <rohithraju488@gmail.com>
Closes: #192 
- Added canonDecode Method
### Decode for the following structs:
- [x] Array
- [x] Boolean
- [x] Number
- [x] Object
- [x] String
- [ ] Value

Rohith-Raju: @gavv Just to check my bearings, how does this look? 

Rohith-Raju: @gavv looking good?
Rohith-Raju: @gavv if good, I'll go ahead with adding docs and moving to other methods
gavv: LGTM
gavv: Regarding docs, ideally code example should be with struct, since it's the main motivation for Decode.

Also, instead of "binds" I'd use term "unmarshals" because we rely on json.Unmarsal here and in particular user has to use json tags in structs.
Rohith-Raju: @gavv Yes, understood.
gavv: One more nitpick.. Usually chain.failed checks are in struct methods, not in canon funcs. Let's use the same approach for Decode too.

That's because all struct methods has similar preamble: call to chain.enrer/leave, and check for chain.failed.
Rohith-Raju: I'll have to change the docs to use struct
Rohith-Raju:  

> Signed-off-by: Rohith Raju [rohithraju488@gmail.com](mailto:rohithraju488@gmail.com) Closes: #192
> 
> * Added canonDecode Method
> 
> ### Decode for the following structs:
> * [x]  Array
> * [ ]  Boolean
> * [ ]  Cookie
> * [ ]  DateTime
> * [ ]  Duration
> * [ ]  Number
> * [ ]  Object
> * [ ]  String
> * [ ]  Value

@gavv  btw does it make sense to add decode for boolean ?
gavv: > btw does it make sense to add decode for boolean

I think yes. For consistency ideally we should add Decode to all json types.

> also am I missing some structs?

Nope.

But it seems we should NOT add Decode to non-json structs: cookie, datetime, duration. Unmarshaling into a struct does not make sense for them.
gavv: Also please add Decode call to TestArray_Failed (same for other structs).
gavv: Also please add test for canonDecode to canon_test.go
Rohith-Raju: > Also please add Decode call to TestArray_Failed (same for other structs).
```
t.Run("failed_chain_Decode", func(t *testing.T) {
		chain := newMockChain(t)
		chain.fail(mockFailure())

		value := []interface{}{123}
		arr := newArray(chain, value)

		var target []interface{}

		check(arr.Decode(target))
	})
```
@gavv Is this fine?
Rohith-Raju: > Also please add test for canonDecode to canon_test.go

@gavv what tests should I add for canonDecode? Pretty much the same tests I added for arrayDecode right? 
gavv: > > Also please add Decode call to TestArray_Failed (same for other structs).
> 
> ```
> t.Run("failed_chain_Decode", func(t *testing.T) {
> 		chain := newMockChain(t)
> 		chain.fail(mockFailure())
> 
> 		value := []interface{}{123}
> 		arr := newArray(chain, value)
> 
> 		var target []interface{}
> 
> 		check(arr.Decode(target))
> 	})
> ```
> 
> @gavv Is this fine?

No. The new check should be integrated into `check` function, because the idea of that unit test it to run similar checks several times of objects prepared differently.
gavv: > @gavv what tests should I add for canonDecode? Pretty much the same tests I added for arrayDecode right?

Oh, you're right, it will be complete duplication of Array tests. Let's not do it then.
gavv: Coverage decreased a bit because we added new uncovered branch in canonDecode: https://coveralls.io/builds/56167786/source?filename=canon.go

This specific branch can't be covered by tests for Array/Object/etc because they always have correct value.

So let's indeed add small test for canonDecode, only for error handling. I'd cover three errors: target is nil, value is not marshallable, value is not unmrashallable into target.
Rohith-Raju: > Coverage decreased a bit because we added new uncovered branch in canonDecode: https://coveralls.io/builds/56167786/source?filename=canon.go
> 
> This specific branch can't be covered by tests for Array/Object/etc because they always have correct value.
> 
> So let's indeed add small test for canonDecode, only for error handling. I'd cover three errors: target is nil, value is not marshallable, value is not unmrashallable into target.

Yes, sure.
Rohith-Raju: While I'm at it do I cover it for Cannon value too ?
<img width="1070" alt="Screen Shot 2023-01-23 at 1 44 07 PM" src="https://user-images.githubusercontent.com/55309324/213992835-588eb23a-dee6-4b20-96c4-9b09dc1e2457.png">


gavv: > While I'm at it, do I cover it for cannonValue too?

It would be awesome if you do it, though I'm not sure it's possible :)
Rohith-Raju: > > While I'm at it, do I cover it for cannonValue too?
> 
> It would be awesome if you do it, though I'm not sure it's possible :)

Oh yes, Not quite possible 😅 
gavv: Hi, I need to merge some commits into master which rework chain interface and affect almost every usage of it. I don't want to bother you with resolving conflicts, so I'll merge this PR first, as it's already good in its current state.

After merging this PR, I'll merge my changes too, resolve conflicts, and then you can create a separate PR for Value.Decode.

Patch:
diff --git a/array.go b/array.go
index 536c77c..1811990 100644
--- a/array.go
+++ b/array.go
@@ -67,6 +67,44 @@ func (a *Array) Raw() []interface{} {
 	return a.value
 }
 
+// Decode unmarshals the underlying value attached to the Array to a target variable.
+// target should be one of these:
+//
+// - pointer to an empty interface
+//
+// - pointer to slice of any type
+//
+// Example:
+//
+//	type S struct{
+//		Foo int `json:foo`
+//	}
+//	value := []interface{}{
+//		map[string]interface{}{
+//				"foo": 123,
+//			},
+//		map[string]interface{}{
+//				"foo": 456,
+//			},
+//	}
+//	array := NewArray(t, value)
+//
+//	var target []S
+//	arr.Decode(&target)
+//
+//	assert.Equal(t, []S{{123},{456}}, target)
+func (a *Array) Decode(target interface{}) *Array {
+	a.chain.enter("Decode()")
+	defer a.chain.leave()
+
+	if a.chain.failed() {
+		return a
+	}
+
+	canonDecode(a.chain, a.value, target)
+	return a
+}
+
 // Path is similar to Value.Path.
 func (a *Array) Path(path string) *Value {
 	a.chain.enter("Path(%q)", path)
diff --git a/boolean.go b/boolean.go
index 3a9dd5e..d524032 100644
--- a/boolean.go
+++ b/boolean.go
@@ -49,6 +49,33 @@ func (b *Boolean) Raw() bool {
 	return b.value
 }
 
+// Decode unmarshals the underlying value attached to the Boolean to a target variable.
+// target should be one of these:
+//
+// - pointer to an empty interface.
+//
+// - pointer to a boolean.
+//
+// Example:
+//
+//	value := NewBoolean(t, true)
+//
+//	var target bool
+//	value.Decode(&target)
+//
+//	assert.Equal(t, true, target)
+func (b *Boolean) Decode(target interface{}) *Boolean {
+	b.chain.enter("Decode()")
+	defer b.chain.leave()
+
+	if b.chain.failed() {
+		return b
+	}
+
+	canonDecode(b.chain, b.value, target)
+	return b
+}
+
 // Path is similar to Value.Path.
 func (b *Boolean) Path(path string) *Value {
 	b.chain.enter("Path(%q)", path)
diff --git a/canon.go b/canon.go
index 3589e7e..ffebd73 100644
--- a/canon.go
+++ b/canon.go
@@ -91,3 +91,38 @@ func canonValue(chain *chain, in interface{}) (interface{}, bool) {
 
 	return out, true
 }
+
+func canonDecode(chain *chain, value interface{}, target interface{}) {
+	if target == nil {
+		chain.fail(AssertionFailure{
+			Type: AssertUsage,
+			Errors: []error{
+				errors.New("unexpected nil target argument"),
+			},
+		})
+		return
+	}
+
+	b, err := json.Marshal(value)
+	if err != nil {
+		chain.fail(AssertionFailure{
+			Type:   AssertValid,
+			Actual: &AssertionValue{value},
+			Errors: []error{
+				errors.New("expected: marshable value"),
+			},
+		})
+		return
+	}
+
+	if err := json.Unmarshal(b, target); err != nil {
+		chain.fail(AssertionFailure{
+			Type:   AssertValid,
+			Actual: &AssertionValue{target},
+			Errors: []error{
+				errors.New("expected: value can be unmarshaled into target argument"),
+			},
+		})
+		return
+	}
+}
diff --git a/number.go b/number.go
index 6024d3b..8ddf051 100644
--- a/number.go
+++ b/number.go
@@ -50,6 +50,33 @@ func (n *Number) Raw() float64 {
 	return n.value
 }
 
+// Decode unmarshals the underlying value attached to the Number to a target variable.
+// target should be one of these:
+//
+// - pointer to empty interface.
+//
+// - pointer to any integer or floating type.
+//
+// Example:
+//
+//	value := NewNumber(t, 123)
+//
+//	var target interface{}
+//	valude.decode(&target)
+//
+//	assert.Equal(t, 123, target)
+func (n *Number) Decode(target interface{}) *Number {
+	n.chain.enter("Decode()")
+	defer n.chain.leave()
+
+	if n.chain.failed() {
+		return n
+	}
+
+	canonDecode(n.chain, n.value, target)
+	return n
+}
+
 // Path is similar to Value.Path.
 func (n *Number) Path(path string) *Value {
 	n.chain.enter("Path(%q)", path)
diff --git a/object.go b/object.go
index b4c4e0b..510122e 100644
--- a/object.go
+++ b/object.go
@@ -68,6 +68,53 @@ func (o *Object) Raw() map[string]interface{} {
 	return o.value
 }
 
+// Decode unmarshals the underlying value attached to the Object to a target variable
+// target should be one of this:
+//
+// - pointer to an empty interface
+//
+// - pointer to a map
+//
+// - pointer to a struct
+//
+// Example:
+//
+//	type S struct{
+//		Foo int                    `json:"foo"`
+//		Bar []interface{}          `json:"bar"`
+//		Baz map[string]interface{} `json:"baz"`
+//		Bat struct{ a int }        `json:"bat"`
+//	}
+//
+//	m := map[string]interface{}{
+//		"foo": 123,
+//		"bar": []interface{}{"123", 234.0},
+//		"baz": map[string]interface{}{
+//			"a": "b",
+//		},
+//		"bat": struct{ a int }{0},
+//	}
+//
+//	value := NewObject(t, value)
+//
+//	var target S
+//	value.Decode(&target)
+//
+//	assert.Equal(t, S{123,[]interface{}{"123", 234.0},
+//		map[string]interface{}{"a": "b"}, struct{ a int }{0},
+//	},target)
+func (o *Object) Decode(target interface{}) *Object {
+	o.chain.enter("Decode()")
+	defer o.chain.leave()
+
+	if o.chain.failed() {
+		return o
+	}
+
+	canonDecode(o.chain, o.value, target)
+	return o
+}
+
 // Path is similar to Value.Path.
 func (o *Object) Path(path string) *Value {
 	o.chain.enter("Path(%q)", path)
diff --git a/string.go b/string.go
index 49a7b8e..36b07d1 100644
--- a/string.go
+++ b/string.go
@@ -56,6 +56,33 @@ func (s *String) Raw() string {
 	return s.value
 }
 
+// Decode unmarshals the underlying value attached to the String to a target variable.
+// target should be one of these:
+//
+// - pointer to an empty interface.
+//
+// - pointer to a string variable.
+//
+// Example:
+//
+//	value := NewString(i, "foo")
+//
+//	var target string
+//	value.Decode(&target)
+//
+//	assert.Equal(t,"foo",target)
+func (s *String) Decode(target interface{}) *String {
+	s.chain.enter("Decode()")
+	defer s.chain.leave()
+
+	if s.chain.failed() {
+		return s
+	}
+
+	canonDecode(s.chain, s.value, target)
+	return s
+}
+
 // Path is similar to Value.Path.
 func (s *String) Path(path string) *Value {
 	s.chain.enter("Path(%q)", path)


Test patch:
diff --git a/array_test.go b/array_test.go
index 2338e1e..1db4bec 100644
--- a/array_test.go
+++ b/array_test.go
@@ -23,6 +23,9 @@ func TestArray_Failed(t *testing.T) {
 		value.First().chain.assertFailed(t)
 		value.Last().chain.assertFailed(t)
 
+		var target interface{}
+		value.Decode(&target)
+
 		value.Empty()
 		value.NotEmpty()
 		value.Equal([]interface{}{})
@@ -115,6 +118,77 @@ func TestArray_Constructors(t *testing.T) {
 	})
 }
 
+func TestArray_Decode(t *testing.T) {
+	t.Run("Decode into slice of empty interfaces", func(t *testing.T) {
+		reporter := newMockReporter(t)
+
+		testValue := []interface{}{"Foo", 123.0}
+		arr := NewArray(reporter, testValue)
+
+		var target []interface{}
+		arr.Decode(&target)
+
+		arr.chain.assertNotFailed(t)
+		assert.Equal(t, testValue, target)
+	})
+	t.Run("Decode into slice of structs", func(t *testing.T) {
+		reporter := newMockReporter(t)
+
+		type S struct {
+			Foo int `json:"foo"`
+		}
+
+		actualStruct := []S{{123}, {456}}
+		testValue := []interface{}{
+			map[string]interface{}{
+				"foo": 123,
+			},
+			map[string]interface{}{
+				"foo": 456,
+			},
+		}
+		arr := NewArray(reporter, testValue)
+
+		var target []S
+		arr.Decode(&target)
+
+		arr.chain.assertNotFailed(t)
+		assert.Equal(t, actualStruct, target)
+	})
+	t.Run("Decode into empty interface", func(t *testing.T) {
+		reporter := newMockReporter(t)
+
+		testValue := []interface{}{"Foo", 123.0}
+		arr := NewArray(reporter, testValue)
+
+		var target interface{}
+		arr.Decode(&target)
+
+		arr.chain.assertNotFailed(t)
+		assert.Equal(t, testValue, target)
+	})
+	t.Run("Target is unmarshable", func(t *testing.T) {
+		reporter := newMockReporter(t)
+
+		testValue := []interface{}{"Foo", 123.0}
+		arr := NewArray(reporter, testValue)
+
+		arr.Decode(123)
+
+		arr.chain.assertFailed(t)
+	})
+	t.Run("Target is nil", func(t *testing.T) {
+		reporter := newMockReporter(t)
+
+		testValue := []interface{}{"Foo", 123.0}
+		arr := NewArray(reporter, testValue)
+
+		arr.Decode(nil)
+
+		arr.chain.assertFailed(t)
+	})
+}
+
 func TestArray_Getters(t *testing.T) {
 	reporter := newMockReporter(t)
 
diff --git a/boolean_test.go b/boolean_test.go
index 7b944e6..bcc518c 100644
--- a/boolean_test.go
+++ b/boolean_test.go
@@ -15,6 +15,9 @@ func TestBoolean_Failed(t *testing.T) {
 	value.Path("$")
 	value.Schema("")
 
+	var target interface{}
+	value.Decode(&target)
+
 	value.Equal(false)
 	value.NotEqual(false)
 	value.True()
@@ -46,6 +49,52 @@ func TestBoolean_Constructors(t *testing.T) {
 	})
 }
 
+func TestBoolean_Decode(t *testing.T) {
+	t.Run("Decode into empty interface", func(t *testing.T) {
+		reporter := newMockReporter(t)
+
+		value := NewBoolean(reporter, true)
+
+		var target interface{}
+		value.Decode(&target)
+
+		value.chain.assertNotFailed(t)
+		assert.Equal(t, true, target)
+	})
+
+	t.Run("Decode into boolean", func(t *testing.T) {
+		reporter := newMockReporter(t)
+
+		value := NewBoolean(reporter, true)
+
+		var target bool
+		value.Decode(&target)
+
+		value.chain.assertNotFailed(t)
+		assert.Equal(t, true, target)
+	})
+
+	t.Run("Target is unmarshable", func(t *testing.T) {
+		reporter := newMockReporter(t)
+
+		value := NewBoolean(reporter, true)
+
+		value.Decode(123)
+
+		value.chain.assertFailed(t)
+	})
+
+	t.Run("Target is nil", func(t *testing.T) {
+		reporter := newMockReporter(t)
+
+		value := NewBoolean(reporter, true)
+
+		value.Decode(nil)
+
+		value.chain.assertFailed(t)
+	})
+}
+
 func TestBoolean_Getters(t *testing.T) {
 	reporter := newMockReporter(t)
 
diff --git a/canon_test.go b/canon_test.go
index 7716b7b..57f293c 100644
--- a/canon_test.go
+++ b/canon_test.go
@@ -123,3 +123,39 @@ func TestCanon_Map(t *testing.T) {
 	chain.assertFailed(t)
 	chain.clearFailed()
 }
+
+func TestCannon_Decode(t *testing.T) {
+	t.Run("target is nill", func(t *testing.T) {
+		chain := newMockChain(t)
+
+		canonDecode(chain, 123, nil)
+
+		chain.assertFailed(t)
+	})
+
+	t.Run("value is not marshallable", func(t *testing.T) {
+		chain := newMockChain(t)
+
+		type S struct {
+			MyFunc func() string
+		}
+
+		value := &S{
+			MyFunc: func() string { return "foo" },
+		}
+
+		var target S
+		canonDecode(chain, value, &target)
+
+		chain.assertFailed(t)
+	})
+
+	t.Run("value is not unmarshallable into target", func(t *testing.T) {
+		chain := newMockChain(t)
+
+		var target int
+		canonDecode(chain, true, target)
+
+		chain.assertFailed(t)
+	})
+}
diff --git a/number_test.go b/number_test.go
index d6de178..5b81b5d 100644
--- a/number_test.go
+++ b/number_test.go
@@ -16,6 +16,9 @@ func TestNumber_Failed(t *testing.T) {
 	value.Path("$")
 	value.Schema("")
 
+	var target interface{}
+	value.Decode(&target)
+
 	value.Equal(0)
 	value.NotEqual(0)
 	value.InDelta(0, 0)
@@ -53,6 +56,63 @@ func TestNumber_Constructors(t *testing.T) {
 	})
 }
 
+func TestNumber_Decode(t *testing.T) {
+	t.Run("Decode into empty interface", func(t *testing.T) {
+		reporter := newMockReporter(t)
+
+		value := NewNumber(reporter, 10.1)
+
+		var target interface{}
+		value.Decode(&target)
+
+		value.chain.assertNotFailed(t)
+		assert.Equal(t, 10.1, target)
+	})
+
+	t.Run("Decode into int variable", func(t *testing.T) {
+		reporter := newMockReporter(t)
+
+		value := NewNumber(reporter, 10)
+
+		var target int
+		value.Decode(&target)
+
+		value.chain.assertNotFailed(t)
+		assert.Equal(t, 10, target)
+	})
+
+	t.Run("Decode into float64 variable", func(t *testing.T) {
+		reporter := newMockReporter(t)
+
+		value := NewNumber(reporter, 10.1)
+
+		var target float64
+		value.Decode(&target)
+
+		value.chain.assertNotFailed(t)
+		assert.Equal(t, 10.1, target)
+	})
+
+	t.Run("Target is unmarshable", func(t *testing.T) {
+		reporter := newMockReporter(t)
+
+		value := NewNumber(reporter, 10.1)
+
+		value.Decode(123)
+
+		value.chain.assertFailed(t)
+	})
+
+	t.Run("Target is nil", func(t *testing.T) {
+		reporter := newMockReporter(t)
+
+		value := NewNumber(reporter, 10.1)
+
+		value.Decode(nil)
+
+		value.chain.assertFailed(t)
+	})
+}
 func TestNumber_Getters(t *testing.T) {
 	reporter := newMockReporter(t)
 
diff --git a/object_test.go b/object_test.go
index d1d01c7..cc50add 100644
--- a/object_test.go
+++ b/object_test.go
@@ -18,6 +18,9 @@ func TestObject_Failed(t *testing.T) {
 		assert.NotNil(t, value.Value("foo"))
 		assert.NotNil(t, value.Iter())
 
+		var target interface{}
+		value.Decode(&target)
+
 		value.Empty()
 		value.NotEmpty()
 		value.Equal(nil)
@@ -110,6 +113,115 @@ func TestObject_Constructors(t *testing.T) {
 	})
 }
 
+func TestObject_Decode(t *testing.T) {
+	t.Run("Decode into empty interface", func(t *testing.T) {
+		reporter := newMockReporter(t)
+
+		m := map[string]interface{}{
+			"foo": 123.0,
+			"bar": []interface{}{"123", 234.0},
+			"baz": map[string]interface{}{
+				"a": "b",
+			},
+		}
+
+		value := NewObject(reporter, m)
+
+		var target interface{}
+		value.Decode(&target)
+
+		value.chain.assertNotFailed(t)
+		assert.Equal(t, target, m)
+	})
+
+	t.Run("Decode into map", func(t *testing.T) {
+		reporter := newMockReporter(t)
+
+		m := map[string]interface{}{
+			"foo": 123.0,
+			"bar": []interface{}{"123", 234.0},
+			"baz": map[string]interface{}{
+				"a": "b",
+			},
+		}
+
+		value := NewObject(reporter, m)
+
+		var target map[string]interface{}
+		value.Decode(&target)
+
+		value.chain.assertNotFailed(t)
+		assert.Equal(t, target, m)
+	})
+
+	t.Run("Decode into struct", func(t *testing.T) {
+		reporter := newMockReporter(t)
+
+		type S struct {
+			Foo int                    `json:"foo"`
+			Bar []interface{}          `json:"bar"`
+			Baz map[string]interface{} `json:"baz"`
+			Bat struct{ a int }        `json:"bat"`
+		}
+
+		m := map[string]interface{}{
+			"foo": 123,
+			"bar": []interface{}{"123", 234.0},
+			"baz": map[string]interface{}{
+				"a": "b",
+			},
+			"bat": struct{ a int }{0},
+		}
+
+		value := NewObject(reporter, m)
+
+		actualStruct := S{123, []interface{}{"123", 234.0},
+			map[string]interface{}{"a": "b"}, struct{ a int }{0},
+		}
+
+		var target S
+		value.Decode(&target)
+
+		value.chain.assertNotFailed(t)
+		assert.Equal(t, target, actualStruct)
+	})
+
+	t.Run("Target is unmarshable", func(t *testing.T) {
+		reporter := newMockReporter(t)
+
+		m := map[string]interface{}{
+			"foo": 123.0,
+			"bar": []interface{}{"123", 234.0},
+			"baz": map[string]interface{}{
+				"a": "b",
+			},
+		}
+
+		value := NewObject(reporter, m)
+
+		value.Decode(123)
+
+		value.chain.assertFailed(t)
+	})
+
+	t.Run("Target is nil", func(t *testing.T) {
+		reporter := newMockReporter(t)
+
+		m := map[string]interface{}{
+			"foo": 123.0,
+			"bar": []interface{}{"123", 234.0},
+			"baz": map[string]interface{}{
+				"a": "b",
+			},
+		}
+
+		value := NewObject(reporter, m)
+
+		value.Decode(nil)
+
+		value.chain.assertFailed(t)
+	})
+}
 func TestObject_Getters(t *testing.T) {
 	reporter := newMockReporter(t)
 
diff --git a/string_test.go b/string_test.go
index 89847c5..1fd671d 100644
--- a/string_test.go
+++ b/string_test.go
@@ -17,6 +17,9 @@ func TestString_Failed(t *testing.T) {
 	value.Path("$")
 	value.Schema("")
 
+	var target interface{}
+	value.Decode(target)
+
 	value.Length()
 	value.AsBoolean()
 	value.AsNumber()
@@ -71,6 +74,52 @@ func TestString_Constructors(t *testing.T) {
 	})
 }
 
+func TestString_Decode(t *testing.T) {
+	t.Run("Decode into empty interface", func(t *testing.T) {
+		reporter := newMockReporter(t)
+
+		value := NewString(reporter, "foo")
+
+		var target interface{}
+		value.Decode(&target)
+
+		value.chain.assertNotFailed(t)
+		assert.Equal(t, "foo", target)
+	})
+
+	t.Run("Decode into string", func(t *testing.T) {
+		reporter := newMockReporter(t)
+
+		value := NewString(reporter, "foo")
+
+		var target string
+		value.Decode(&target)
+
+		value.chain.assertNotFailed(t)
+		assert.Equal(t, "foo", target)
+	})
+
+	t.Run("Target is unmarshable", func(t *testing.T) {
+		reporter := newMockReporter(t)
+
+		value := NewString(reporter, "foo")
+
+		value.Decode(123)
+
+		value.chain.assertFailed(t)
+	})
+
+	t.Run("Target is nil", func(t *testing.T) {
+		reporter := newMockReporter(t)
+
+		value := NewString(reporter, "foo")
+
+		value.Decode(nil)
+
+		value.chain.assertFailed(t)
+	})
+}
+
 func TestString_Getters(t *testing.T) {
 	reporter := newMockReporter(t)