Repository: gavv/httpexpect (go)

Issue:
Add unit tests for reporters
Add unit tests for reporters defined in reporter.go. To achieve this, we'll need a mock that implements assert.TestingT and require.TestingT.

Pull request description:
Add unit tests for reporters
Closes #196 

Patch:
diff --git a/README.md b/README.md
index e23ce50..0c69efb 100644
--- a/README.md
+++ b/README.md
@@ -220,6 +220,27 @@ for _, private := range repos.Path("$..private").Array().Iter() {
 }
 ```
 
+##### JSON decoding
+
+```go
+type User struct {
+	Name   string `json:"name"`
+	Age    int    `json:"age"`
+	Gender string `json:"gender"`
+}
+
+var user User
+e.GET("/user").
+	Expect().
+	Status(http.StatusOK).
+	JSON().
+	Decode(&user)
+	
+if user.Name != "octocat" {
+	t.Fail()
+}
+```
+
 ##### Forms
 
 ```go
diff --git a/array.go b/array.go
index 1e9147b..0fb2301 100644
--- a/array.go
+++ b/array.go
@@ -73,8 +73,8 @@ func (a *Array) Raw() []interface{} {
 // 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 a slice of any type
+//   - pointer to an empty interface
+//   - pointer to a slice of any type
 //
 // Example:
 //
diff --git a/boolean.go b/boolean.go
index 74b74fa..f502f81 100644
--- a/boolean.go
+++ b/boolean.go
@@ -52,8 +52,8 @@ func (b *Boolean) Raw() bool {
 // 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
+//   - pointer to an empty interface
+//   - pointer to a boolean
 //
 // Example:
 //
diff --git a/number.go b/number.go
index fe5ac31..71703b4 100644
--- a/number.go
+++ b/number.go
@@ -53,8 +53,8 @@ func (n *Number) Raw() float64 {
 // Decode unmarshals the underlying value attached to the Number to a target variable.
 // target should be one of these:
 //
-// - pointer to an empty interface
-// - pointer to any integer or floating type
+//   - pointer to an empty interface
+//   - pointer to any integer or floating type
 //
 // Example:
 //
diff --git a/object.go b/object.go
index aebcb50..b51c76c 100644
--- a/object.go
+++ b/object.go
@@ -74,9 +74,9 @@ func (o *Object) Raw() map[string]interface{} {
 // 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
+//   - pointer to an empty interface
+//   - pointer to a map
+//   - pointer to a struct
 //
 // Example:
 //
@@ -84,7 +84,7 @@ func (o *Object) Raw() map[string]interface{} {
 //		Foo int                    `json:"foo"`
 //		Bar []interface{}          `json:"bar"`
 //		Baz map[string]interface{} `json:"baz"`
-//		Bat struct{ a int }        `json:"bat"`
+//		Bat struct{ A int }        `json:"bat"`
 //	}
 //
 //	m := map[string]interface{}{
@@ -93,7 +93,7 @@ func (o *Object) Raw() map[string]interface{} {
 //		"baz": map[string]interface{}{
 //			"a": "b",
 //		},
-//		"bat": struct{ a int }{0},
+//		"bat": struct{ A int }{123},
 //	}
 //
 //	value := NewObject(t, value)
@@ -102,7 +102,7 @@ func (o *Object) Raw() map[string]interface{} {
 //	value.Decode(&target)
 //
 //	assert.Equal(t, S{123,[]interface{}{"123", 234.0},
-//		map[string]interface{}{"a": "b"}, struct{ a int }{0},
+//		map[string]interface{}{"a": "b"}, struct{ A int }{123},
 //	}, target)
 func (o *Object) Decode(target interface{}) *Object {
 	opChain := o.chain.enter("Decode()")
diff --git a/string.go b/string.go
index c0e7513..ff6258b 100644
--- a/string.go
+++ b/string.go
@@ -59,8 +59,8 @@ func (s *String) Raw() string {
 // 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
+//   - pointer to an empty interface
+//   - pointer to a string
 //
 // Example:
 //
diff --git a/value.go b/value.go
index 592ab4c..fe9be15 100644
--- a/value.go
+++ b/value.go
@@ -75,6 +75,41 @@ func (v *Value) Raw() interface{} {
 	return v.value
 }
 
+// Decode unmarshals the underlying value attached to the Object to a target variable
+// target should be pointer to any type.
+//
+// Example:
+//
+//	type S struct {
+//		Foo int             `json:"foo"`
+//		Bar []interface{}   `json:"bar"`
+//		Baz struct{ A int } `json:"baz"`
+//	}
+//
+//	m := map[string]interface{}{
+//		"foo": 123,
+//		"bar": []interface{}{"123", 456.0},
+//		"baz": struct{ A int }{123},
+//	}
+//
+//	value = NewValue(reporter,m)
+//
+//	var target S
+//	value.Decode(&target)
+//
+//	assert.Equal(t, S{123, []interface{}{"123", 456.0}, struct{ A int }{123}, target})
+func (v *Value) Decode(target interface{}) *Value {
+	opChain := v.chain.enter("Decode()")
+	defer opChain.leave()
+
+	if opChain.failed() {
+		return v
+	}
+
+	canonDecode(opChain, v.value, target)
+	return v
+}
+
 // Alias returns a new Value object with alias.
 // When a test of Value object with alias is failed,
 // an assertion is displayed as a chain starting from the alias.


Test patch:
diff --git a/object_test.go b/object_test.go
index c27d288..391ad3a 100644
--- a/object_test.go
+++ b/object_test.go
@@ -162,7 +162,7 @@ func TestObject_Decode(t *testing.T) {
 			Foo int                    `json:"foo"`
 			Bar []interface{}          `json:"bar"`
 			Baz map[string]interface{} `json:"baz"`
-			Bat struct{ a int }        `json:"bat"`
+			Bat struct{ A int }        `json:"bat"`
 		}
 
 		m := map[string]interface{}{
@@ -171,13 +171,16 @@ func TestObject_Decode(t *testing.T) {
 			"baz": map[string]interface{}{
 				"a": "b",
 			},
-			"bat": struct{ a int }{0},
+			"bat": struct{ A int }{123},
 		}
 
 		value := NewObject(reporter, m)
 
-		actualStruct := S{123, []interface{}{"123", 234.0},
-			map[string]interface{}{"a": "b"}, struct{ a int }{0},
+		actualStruct := S{
+			Foo: 123,
+			Bar: []interface{}{"123", 234.0},
+			Baz: map[string]interface{}{"a": "b"},
+			Bat: struct{ A int }{123},
 		}
 
 		var target S
diff --git a/reporter_test.go b/reporter_test.go
index 48e4564..bad6609 100644
--- a/reporter_test.go
+++ b/reporter_test.go
@@ -24,3 +24,40 @@ func TestReporter_FatalReporter(t *testing.T) {
 		assert.True(t, mockBackend.fatalfInvoked)
 	})
 }
+
+type mockAssertT struct {
+	failInvoked bool
+}
+
+func (m *mockAssertT) Errorf(format string, args ...interface{}) {
+	m.failInvoked = true
+}
+
+func TestReporter_AssertReporter(t *testing.T) {
+	mockBackend := &mockAssertT{}
+	reporter := NewAssertReporter(mockBackend)
+
+	t.Run("Errorf", func(t *testing.T) {
+		reporter.Errorf("Test failed with backend: %v", mockBackend)
+		assert.True(t, mockBackend.failInvoked)
+	})
+}
+
+type mockRequireT struct {
+	testing.T
+	failNowInvoked bool
+}
+
+func (m *mockRequireT) FailNow() {
+	m.failNowInvoked = true
+}
+
+func TestReporter_RequireReporter(t *testing.T) {
+	mockBackend := &mockRequireT{}
+	reporter := NewRequireReporter(mockBackend)
+
+	t.Run("Errorf", func(t *testing.T) {
+		reporter.Errorf("Test failed with backend: %v", mockBackend)
+		assert.True(t, mockBackend.failNowInvoked)
+	})
+}
diff --git a/value_test.go b/value_test.go
index 286d448..a44c05e 100644
--- a/value_test.go
+++ b/value_test.go
@@ -22,6 +22,9 @@ func TestValue_Failed(t *testing.T) {
 
 	assert.NotNil(t, value.Path("/"))
 
+	var target interface{}
+	value.Decode(target)
+
 	assert.NotNil(t, value.Object())
 	assert.NotNil(t, value.Array())
 	assert.NotNil(t, value.String())
@@ -68,6 +71,70 @@ func TestValue_Constructors(t *testing.T) {
 	})
 }
 
+func TestValue_Decode(t *testing.T) {
+	t.Run("Decode into empty interface", func(t *testing.T) {
+		reporter := newMockReporter(t)
+
+		value := NewValue(reporter, 123.0)
+
+		var target interface{}
+		value.Decode(&target)
+
+		value.chain.assertNotFailed(t)
+		assert.Equal(t, 123.0, target)
+	})
+
+	t.Run("Decode into struct", func(t *testing.T) {
+		reporter := newMockReporter(t)
+
+		type S struct {
+			Foo int             `json:"foo"`
+			Bar []interface{}   `json:"bar"`
+			Baz struct{ A int } `json:"baz"`
+		}
+
+		m := map[string]interface{}{
+			"foo": 123,
+			"bar": []interface{}{"123", 456.0},
+			"baz": struct{ A int }{123},
+		}
+
+		value := NewValue(reporter, m)
+
+		actualStruct := S{
+			123,
+			[]interface{}{"123", 456.0},
+			struct{ A int }{123},
+		}
+
+		var target S
+		value.Decode(&target)
+
+		value.chain.assertNotFailed(t)
+		assert.Equal(t, target, actualStruct)
+	})
+
+	t.Run("Target is nil", func(t *testing.T) {
+		reporter := newMockReporter(t)
+
+		value := NewValue(reporter, 123)
+
+		value.Decode(nil)
+
+		value.chain.failed()
+	})
+
+	t.Run("Target is unmarshable", func(t *testing.T) {
+		reporter := newMockReporter(t)
+
+		value := NewValue(reporter, 123)
+
+		value.Decode(123)
+
+		value.chain.failed()
+	})
+}
+
 func TestValue_Alias(t *testing.T) {
 	reporter := newMockReporter(t)