Repository: gavv/httpexpect
Pull request #347 (2023-03-23 22:57:25)

Refactor Number and String tests to use chain.assertOK
Issue: #345 

Description:

Refactor tests
- string_test
- number_test

coveralls: 
[![Coverage Status](https://coveralls.io/builds/58183883/badge)](https://coveralls.io/builds/58183883)

Coverage: 95.505%. Remained the same when pulling **b6e2fefe333f53406df2060f50f26f00452595da on tsukasaI:345-use-assertOK-for-number-and-string-test** into **1bd520a64629195f78a96c8dad5fd30256e82820 on gavv:master**.


Patch:
diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index dbba942..51f0a3c 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -19,7 +19,7 @@ jobs:
 
     strategy:
       matrix:
-        go: [1.16, 1.x]
+        go: [1.17, 1.x]
 
     name: Go ${{ matrix.go }}
     steps:
diff --git a/chain.go b/chain.go
index 152f9e2..18926ac 100644
--- a/chain.go
+++ b/chain.go
@@ -100,6 +100,13 @@ const (
 	flagFailedChildren                          // fail() was called on any child
 )
 
+type chainResult bool
+
+const (
+	success chainResult = true
+	failure chainResult = false
+)
+
 // Construct chain using config.
 func newChainWithConfig(name string, config Config) *chain {
 	config.validate()
@@ -443,8 +450,7 @@ func (c *chain) treeFailed() bool {
 	return c.flags&(flagFailed|flagFailedChildren) != 0
 }
 
-// Set failure flag.
-// For tests.
+// DEPRECATED: use setFlags
 func (c *chain) setFailed() {
 	c.mu.Lock()
 	defer c.mu.Unlock()
@@ -452,8 +458,16 @@ func (c *chain) setFailed() {
 	c.flags |= flagFailed
 }
 
-// Clear failure flags.
+// Set chain flags.
 // For tests.
+func (c *chain) setFlags(flags chainFlags) {
+	c.mu.Lock()
+	defer c.mu.Unlock()
+
+	c.flags = flags
+}
+
+// DEPRECATED: use clear
 func (c *chain) clearFailed() {
 	c.mu.Lock()
 	defer c.mu.Unlock()
@@ -461,20 +475,26 @@ func (c *chain) clearFailed() {
 	c.flags &= ^(flagFailed | flagFailedChildren)
 }
 
-// Report failure unless chain is not failed.
+// Clear failure flags.
 // For tests.
+func (c *chain) clear() {
+	c.mu.Lock()
+	defer c.mu.Unlock()
+
+	c.flags &= ^(flagFailed | flagFailedChildren)
+}
+
+// DEPRECATED: use assert
 func (c *chain) assertNotFailed(t testing.TB) {
 	c.assertOK(t, true)
 }
 
-// Report failure unless chain is failed.
-// For tests.
+// DEPRECATED: use assert
 func (c *chain) assertFailed(t testing.TB) {
 	c.assertOK(t, false)
 }
 
-// Report failure unless chain has specified state.
-// For tests.
+// DEPRECATED: use assert
 func (c *chain) assertOK(t testing.TB, expectOK bool) {
 	c.mu.Lock()
 	defer c.mu.Unlock()
@@ -488,6 +508,23 @@ func (c *chain) assertOK(t testing.TB, expectOK bool) {
 	}
 }
 
+// Report failure unless chain has specified state.
+// For tests.
+func (c *chain) assert(t testing.TB, result chainResult) {
+	c.mu.Lock()
+	defer c.mu.Unlock()
+
+	switch result {
+	case success:
+		assert.Equal(t, chainFlags(0), c.flags&flagFailed,
+			"expected: chain is ok")
+
+	case failure:
+		assert.NotEqual(t, chainFlags(0), c.flags&flagFailed,
+			"expected: chain is not ok")
+	}
+}
+
 // Report failure unless chain has specified flags.
 // For tests.
 func (c *chain) assertFlags(t testing.TB, flags chainFlags) {
diff --git a/go.mod b/go.mod
index 739f8b7..9758db0 100644
--- a/go.mod
+++ b/go.mod
@@ -1,12 +1,13 @@
 module github.com/gavv/httpexpect/v2
 
-go 1.16
+go 1.17
 
 require (
 	github.com/ajg/form v1.5.1
 	github.com/fasthttp/websocket v1.4.3-rc.6
 	github.com/fatih/color v1.13.0
 	github.com/fatih/structs v1.1.0
+	github.com/gobwas/glob v0.2.3
 	github.com/google/go-querystring v1.1.0
 	github.com/gorilla/websocket v1.4.2
 	github.com/imkira/go-interpol v1.1.0
@@ -18,19 +19,25 @@ require (
 	github.com/xeipuuv/gojsonschema v1.2.0
 	github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0
 	github.com/yudai/gojsondiff v1.0.0
+	golang.org/x/net v0.7.0
 	moul.io/http2curl/v2 v2.3.0
 )
 
 require (
+	github.com/andybalholm/brotli v1.0.4 // indirect
 	github.com/davecgh/go-spew v1.1.1 // indirect
-	github.com/gobwas/glob v0.2.3
+	github.com/klauspost/compress v1.15.0 // indirect
 	github.com/mattn/go-colorable v0.1.13 // indirect
 	github.com/onsi/ginkgo v1.10.1 // indirect
 	github.com/onsi/gomega v1.7.0 // indirect
+	github.com/pmezard/go-difflib v1.0.0 // indirect
+	github.com/savsgio/gotils v0.0.0-20210617111740-97865ed5a873 // indirect
+	github.com/sergi/go-diff v1.0.0 // indirect
+	github.com/valyala/bytebufferpool v1.0.0 // indirect
 	github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
+	github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
 	github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect
 	github.com/yudai/pp v2.0.1+incompatible // indirect
-	golang.org/x/net v0.0.0-20220722155237-a158d28d115b
-	golang.org/x/text v0.7.0 // indirect
+	golang.org/x/sys v0.5.0 // indirect
 	gopkg.in/yaml.v2 v2.4.0 // indirect
 )
diff --git a/go.sum b/go.sum
index 6b7653c..9abd0a1 100644
--- a/go.sum
+++ b/go.sum
@@ -104,8 +104,9 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
 golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
-golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0=
 golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
+golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -125,10 +126,12 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU=
 golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
+golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=


Test patch:
diff --git a/array_test.go b/array_test.go
index 368fe47..4f95101 100644
--- a/array_test.go
+++ b/array_test.go
@@ -9,19 +9,19 @@ import (
 
 func TestArray_FailedChain(t *testing.T) {
 	check := func(value *Array) {
-		value.chain.assertFailed(t)
+		value.chain.assert(t, failure)
 
-		value.Path("$").chain.assertFailed(t)
+		value.Path("$").chain.assert(t, failure)
 		value.Schema("")
 		value.Alias("foo")
 
 		var target interface{}
 		value.Decode(&target)
 
-		value.Length().chain.assertFailed(t)
-		value.Value(0).chain.assertFailed(t)
-		value.First().chain.assertFailed(t)
-		value.Last().chain.assertFailed(t)
+		value.Length().chain.assert(t, failure)
+		value.Value(0).chain.assert(t, failure)
+		value.First().chain.assert(t, failure)
+		value.Last().chain.assert(t, failure)
 
 		value.IsEmpty()
 		value.NotEmpty()
@@ -72,9 +72,7 @@ func TestArray_FailedChain(t *testing.T) {
 	}
 
 	t.Run("failed chain", func(t *testing.T) {
-		chain := newMockChain(t)
-		chain.setFailed()
-
+		chain := newFailedChain(t)
 		value := newArray(chain, []interface{}{})
 
 		check(value)
@@ -82,16 +80,13 @@ func TestArray_FailedChain(t *testing.T) {
 
 	t.Run("nil value", func(t *testing.T) {
 		chain := newMockChain(t)
-
 		value := newArray(chain, nil)
 
 		check(value)
 	})
 
 	t.Run("failed chain, nil value", func(t *testing.T) {
-		chain := newMockChain(t)
-		chain.setFailed()
-
+		chain := newFailedChain(t)
 		value := newArray(chain, nil)
 
 		check(value)
@@ -107,7 +102,7 @@ func TestArray_Constructors(t *testing.T) {
 		value := NewArray(reporter, testValue)
 
 		value.IsEqual(testValue)
-		value.chain.assertNotFailed(t)
+		value.chain.assert(t, success)
 	})
 
 	t.Run("config", func(t *testing.T) {
@@ -118,7 +113,7 @@ func TestArray_Constructors(t *testing.T) {
 		}, testValue)
 
 		value.IsEqual(testValue)
-		value.chain.assertNotFailed(t)
+		value.chain.assert(t, success)
 	})
 
 	t.Run("chain", func(t *testing.T) {
@@ -135,8 +130,8 @@ func TestArray_Constructors(t *testing.T) {
 
 		value := NewArray(reporter, nil)
 
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 	})
 }
 
@@ -150,7 +145,7 @@ func TestArray_Decode(t *testing.T) {
 		var target interface{}
 		arr.Decode(&target)
 
-		arr.chain.assertNotFailed(t)
+		arr.chain.assert(t, success)
 		assert.Equal(t, testValue, target)
 	})
 
@@ -163,7 +158,7 @@ func TestArray_Decode(t *testing.T) {
 		var target []interface{}
 		arr.Decode(&target)
 
-		arr.chain.assertNotFailed(t)
+		arr.chain.assert(t, success)
 		assert.Equal(t, testValue, target)
 	})
 
@@ -188,7 +183,7 @@ func TestArray_Decode(t *testing.T) {
 		var target []S
 		arr.Decode(&target)
 
-		arr.chain.assertNotFailed(t)
+		arr.chain.assert(t, success)
 		assert.Equal(t, actualStruct, target)
 	})
 
@@ -200,7 +195,7 @@ func TestArray_Decode(t *testing.T) {
 
 		arr.Decode(123)
 
-		arr.chain.assertFailed(t)
+		arr.chain.assert(t, failure)
 	})
 
 	t.Run("target is nil", func(t *testing.T) {
@@ -211,7 +206,7 @@ func TestArray_Decode(t *testing.T) {
 
 		arr.Decode(nil)
 
-		arr.chain.assertFailed(t)
+		arr.chain.assert(t, failure)
 	})
 }
 
@@ -242,40 +237,40 @@ func TestArray_Getters(t *testing.T) {
 		value := NewArray(reporter, data)
 
 		assert.Equal(t, data, value.Raw())
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		assert.Equal(t, data, value.Path("$").Raw())
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.Schema(`{"type": "array"}`)
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.Schema(`{"type": "object"}`)
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		assert.Equal(t, 0.0, value.Length().Raw())
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		assert.NotNil(t, value.Value(0))
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		assert.NotNil(t, value.First())
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		assert.NotNil(t, value.Last())
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		assert.NotNil(t, value.Iter())
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 	})
 
 	t.Run("not empty", func(t *testing.T) {
@@ -286,45 +281,45 @@ func TestArray_Getters(t *testing.T) {
 		value := NewArray(reporter, data)
 
 		assert.Equal(t, data, value.Raw())
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		assert.Equal(t, data, value.Path("$").Raw())
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.Schema(`{"type": "array"}`)
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.Schema(`{"type": "object"}`)
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		assert.Equal(t, 2.0, value.Length().Raw())
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		assert.Equal(t, "foo", value.Value(0).Raw())
 		assert.Equal(t, 123.0, value.Value(1).Raw())
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		assert.Equal(t, nil, value.Value(2).Raw())
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		assert.Equal(t, "foo", value.First().Raw())
 		assert.Equal(t, 123.0, value.Last().Raw())
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		it := value.Iter()
 		assert.Equal(t, 2, len(it))
 		assert.Equal(t, "foo", it[0].Raw())
 		assert.Equal(t, 123.0, it[1].Raw())
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 	})
 }
 
@@ -335,12 +330,12 @@ func TestArray_IsEmpty(t *testing.T) {
 		value := NewArray(reporter, []interface{}{})
 
 		value.IsEmpty()
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotEmpty()
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 	})
 
 	t.Run("one empty element", func(t *testing.T) {
@@ -349,12 +344,12 @@ func TestArray_IsEmpty(t *testing.T) {
 		value := NewArray(reporter, []interface{}{""})
 
 		value.IsEmpty()
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotEmpty()
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 	})
 }
 
@@ -367,20 +362,20 @@ func TestArray_IsEqual(t *testing.T) {
 		assert.Equal(t, []interface{}{}, value.Raw())
 
 		value.IsEqual([]interface{}{})
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotEqual([]interface{}{})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.IsEqual([]interface{}{""})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotEqual([]interface{}{""})
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 	})
 
 	t.Run("not empty", func(t *testing.T) {
@@ -391,36 +386,36 @@ func TestArray_IsEqual(t *testing.T) {
 		assert.Equal(t, []interface{}{"foo", "bar"}, value.Raw())
 
 		value.IsEqual([]interface{}{})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotEqual([]interface{}{})
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.IsEqual([]interface{}{"foo"})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotEqual([]interface{}{"foo"})
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.IsEqual([]interface{}{"bar", "foo"})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotEqual([]interface{}{"bar", "foo"})
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.IsEqual([]interface{}{"foo", "bar"})
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotEqual([]interface{}{"foo", "bar"})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 	})
 
 	t.Run("strings", func(t *testing.T) {
@@ -429,20 +424,20 @@ func TestArray_IsEqual(t *testing.T) {
 		value := NewArray(reporter, []interface{}{"foo", "bar"})
 
 		value.IsEqual([]string{"foo", "bar"})
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.IsEqual([]string{"bar", "foo"})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotEqual([]string{"foo", "bar"})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotEqual([]string{"bar", "foo"})
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 	})
 
 	t.Run("numbers", func(t *testing.T) {
@@ -451,20 +446,20 @@ func TestArray_IsEqual(t *testing.T) {
 		value := NewArray(reporter, []interface{}{123, 456})
 
 		value.IsEqual([]int{123, 456})
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.IsEqual([]int{456, 123})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotEqual([]int{123, 456})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotEqual([]int{456, 123})
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 	})
 
 	t.Run("struct", func(t *testing.T) {
@@ -483,20 +478,20 @@ func TestArray_IsEqual(t *testing.T) {
 		})
 
 		value.IsEqual([]S{{123}, {456}})
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.IsEqual([]S{{456}, {123}})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotEqual([]S{{123}, {456}})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotEqual([]S{{456}, {123}})
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 	})
 
 	t.Run("canonization", func(t *testing.T) {
@@ -512,12 +507,12 @@ func TestArray_IsEqual(t *testing.T) {
 		assert.Equal(t, []interface{}{123.0, 456.0}, value.Raw())
 
 		value.IsEqual(myArray{myInt(123), 456.0})
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotEqual(myArray{myInt(123), 456.0})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 	})
 
 	t.Run("invalid argument", func(t *testing.T) {
@@ -526,20 +521,20 @@ func TestArray_IsEqual(t *testing.T) {
 		value := NewArray(reporter, []interface{}{})
 
 		value.IsEqual(nil)
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotEqual(nil)
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.IsEqual(func() {})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotEqual(func() {})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 	})
 }
 
@@ -550,52 +545,52 @@ func TestArray_IsEqualUnordered(t *testing.T) {
 		value := NewArray(reporter, []interface{}{123, "foo"})
 
 		value.IsEqualUnordered([]interface{}{123})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotEqualUnordered([]interface{}{123})
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.IsEqualUnordered([]interface{}{"foo"})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotEqualUnordered([]interface{}{"foo"})
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.IsEqualUnordered([]interface{}{123, "foo", "foo"})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotEqualUnordered([]interface{}{123, "foo", "foo"})
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.IsEqualUnordered([]interface{}{123, "foo"})
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotEqualUnordered([]interface{}{123, "foo"})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.IsEqualUnordered([]interface{}{"foo", 123})
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotEqualUnordered([]interface{}{"foo", 123})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.IsEqualUnordered([]interface{}{"foo", 1234})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotEqualUnordered([]interface{}{"foo", 1234})
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 	})
 
@@ -605,44 +600,44 @@ func TestArray_IsEqualUnordered(t *testing.T) {
 		value := NewArray(reporter, []interface{}{123, "foo", "foo"})
 
 		value.IsEqualUnordered([]interface{}{123, "foo"})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotEqualUnordered([]interface{}{123, "foo"})
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.IsEqualUnordered([]interface{}{123, 123, "foo"})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotEqualUnordered([]interface{}{123, 123, "foo"})
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.IsEqualUnordered([]interface{}{123, "foo", "foo"})
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotEqualUnordered([]interface{}{123, "foo", "foo"})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.IsEqualUnordered([]interface{}{"foo", 123, "foo"})
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotEqualUnordered([]interface{}{"foo", 123, "foo"})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.IsEqualUnordered([]interface{}{123})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotEqualUnordered([]interface{}{123})
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 	})
 
 	t.Run("canonization", func(t *testing.T) {
@@ -658,12 +653,12 @@ func TestArray_IsEqualUnordered(t *testing.T) {
 		assert.Equal(t, []interface{}{123.0, 456.0, "foo"}, value.Raw())
 
 		value.IsEqualUnordered(myArray{myInt(456), 123.0, "foo"})
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotEqualUnordered(myArray{myInt(456), 123.0, "foo"})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 	})
 
 	t.Run("invalid argument", func(t *testing.T) {
@@ -672,20 +667,20 @@ func TestArray_IsEqualUnordered(t *testing.T) {
 		value := NewArray(reporter, []interface{}{})
 
 		value.IsEqualUnordered(nil)
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotEqualUnordered(nil)
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.IsEqualUnordered(func() {})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotEqualUnordered(func() {})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 	})
 }
 
@@ -696,44 +691,44 @@ func TestArray_InList(t *testing.T) {
 		value := NewArray(reporter, []interface{}{"foo", "bar"})
 
 		value.InList("foo", "bar")
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotInList("foo", "bar")
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.InList([]interface{}{})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotInList([]interface{}{})
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.InList([]interface{}{"bar", "foo"})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotInList([]interface{}{"bar", "foo"})
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.InList([]interface{}{"bar", "foo"}, []interface{}{"foo", "bar"})
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotInList([]interface{}{"bar", "foo"}, []interface{}{"foo", "bar"})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.InList([]interface{}{"bar", "foo"}, []interface{}{"FOO", "BAR"})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotInList([]interface{}{"bar", "foo"}, []interface{}{"FOO", "BAR"})
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 	})
 
 	t.Run("canonization", func(t *testing.T) {
@@ -761,8 +756,8 @@ func TestArray_InList(t *testing.T) {
 			},
 			myMap{"a": "b"},
 		})
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotInList(myArray{
 			myInt(123.0),
@@ -773,24 +768,24 @@ func TestArray_InList(t *testing.T) {
 			},
 			myMap{"a": "b"},
 		})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.InList(myArray{123.0, 456.0, myArray{789.0, 567.0}, myMap{"a": "b"}})
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotInList(myArray{123.0, 456.0, myArray{789.0, 567.0}, myMap{"a": "b"}})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.InList(myArray{myInt(123), 456.0, myArray{myInt(789), 567.0}, myMap{"a": "b"}})
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotInList(myArray{myInt(123), 456.0, myArray{myInt(789), 567.0}, myMap{"a": "b"}})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 	})
 
 	t.Run("not array", func(t *testing.T) {
@@ -799,20 +794,20 @@ func TestArray_InList(t *testing.T) {
 		value := NewArray(reporter, []interface{}{"foo", "bar"})
 
 		value.InList([]interface{}{"bar", "foo"}, "NOT ARRAY")
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotInList([]interface{}{"bar", "foo"}, "NOT ARRAY")
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.InList([]interface{}{"foo", "bar"}, "NOT ARRAY")
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotInList([]interface{}{"foo", "bar"}, "NOT ARRAY")
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 	})
 
 	t.Run("invalid argument", func(t *testing.T) {
@@ -821,28 +816,28 @@ func TestArray_InList(t *testing.T) {
 		value := NewArray(reporter, []interface{}{})
 
 		value.InList()
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotInList()
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.InList(nil)
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotInList(nil)
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.InList(func() {})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotInList(func() {})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 	})
 }
 
@@ -853,44 +848,44 @@ func TestArray_ConsistsOf(t *testing.T) {
 		value := NewArray(reporter, []interface{}{123, "foo"})
 
 		value.ConsistsOf(123)
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotConsistsOf(123)
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.ConsistsOf("foo")
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotConsistsOf("foo")
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.ConsistsOf("foo", 123)
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotConsistsOf("foo", 123)
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.ConsistsOf(123, "foo", "foo")
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotConsistsOf(123, "foo", "foo")
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.ConsistsOf(123, "foo")
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotConsistsOf(123, "foo")
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 	})
 
@@ -906,12 +901,12 @@ func TestArray_ConsistsOf(t *testing.T) {
 		assert.Equal(t, []interface{}{123.0, 456.0}, value.Raw())
 
 		value.ConsistsOf(myInt(123), 456.0)
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotConsistsOf(myInt(123), 456.0)
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 	})
 
 	t.Run("invalid argument", func(t *testing.T) {
@@ -920,12 +915,12 @@ func TestArray_ConsistsOf(t *testing.T) {
 		value := NewArray(reporter, []interface{}{})
 
 		value.ConsistsOf(func() {})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotConsistsOf(func() {})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 	})
 }
 
@@ -936,48 +931,48 @@ func TestArray_Contains(t *testing.T) {
 		value := NewArray(reporter, []interface{}{123, "foo"})
 
 		value.Contains(123)
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotContains(123)
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.Contains("foo", 123)
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotContains("foo", 123)
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.Contains("foo", "foo")
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotContains("foo", "foo")
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.Contains(123, "foo", "FOO")
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotContains(123, "foo", "FOO")
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotContains("FOO")
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.Contains([]interface{}{123, "foo"})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotContains([]interface{}{123, "foo"})
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 	})
 
 	t.Run("canonization", func(t *testing.T) {
@@ -992,20 +987,20 @@ func TestArray_Contains(t *testing.T) {
 		assert.Equal(t, []interface{}{123.0, 456.0}, value.Raw())
 
 		value.Contains(myInt(123))
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotContains(myInt(123))
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.Contains(456.0)
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotContains(456.0)
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 	})
 
 	t.Run("invalid argument", func(t *testing.T) {
@@ -1014,12 +1009,12 @@ func TestArray_Contains(t *testing.T) {
 		value := NewArray(reporter, []interface{}{})
 
 		value.Contains(func() {})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotContains(func() {})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 	})
 }
 
@@ -1030,52 +1025,52 @@ func TestArray_ContainsAll(t *testing.T) {
 		value := NewArray(reporter, []interface{}{123, "foo"})
 
 		value.ContainsAll(123)
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotContainsAll(123)
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.ContainsAll("foo")
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotContainsAll("foo")
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.ContainsAll("FOO")
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotContainsAll("FOO")
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.ContainsAll(123, "foo")
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotContainsAll(123, "foo")
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.ContainsAll("foo", "foo")
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotContainsAll("foo", "foo")
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.ContainsAll(123, "foo", "FOO")
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotContainsAll(123, "foo", "FOO")
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 	})
 
 	t.Run("canonization", func(t *testing.T) {
@@ -1090,12 +1085,12 @@ func TestArray_ContainsAll(t *testing.T) {
 		assert.Equal(t, []interface{}{123.0, 456.0}, value.Raw())
 
 		value.ContainsAll(myInt(123), 456.0)
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotContainsAll(myInt(123), 456.0)
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 	})
 
 	t.Run("invalid argument", func(t *testing.T) {
@@ -1104,12 +1099,12 @@ func TestArray_ContainsAll(t *testing.T) {
 		value := NewArray(reporter, []interface{}{})
 
 		value.ContainsAll(func() {})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotContainsAll(func() {})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 	})
 }
 
@@ -1120,44 +1115,44 @@ func TestArray_ContainsAny(t *testing.T) {
 		value := NewArray(reporter, []interface{}{123, "foo"})
 
 		value.ContainsAny(123)
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotContainsAny(123)
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.ContainsAny("foo", 123)
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotContainsAny("foo", 123)
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.ContainsAny("foo", "foo")
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotContainsAny("foo", "foo")
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.ContainsAny(123, "foo", "FOO")
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotContainsAny(123, "foo", "FOO")
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.ContainsAny("FOO")
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotContainsAny("FOO")
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 	})
 
 	t.Run("canonization", func(t *testing.T) {
@@ -1171,20 +1166,20 @@ func TestArray_ContainsAny(t *testing.T) {
 		value := NewArray(reporter, []interface{}{123, 789, "foo", []interface{}{567, 456}})
 
 		value.ContainsAny(myInt(123), 789.0)
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotContainsAny(myInt(123), 789.0)
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.ContainsAny(myArray{567.0, 456.0})
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotContainsAny(myArray{567.0, 456.0})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 	})
 
 	t.Run("invalid argument", func(t *testing.T) {
@@ -1193,12 +1188,12 @@ func TestArray_ContainsAny(t *testing.T) {
 		value := NewArray(reporter, []interface{}{})
 
 		value.ContainsAny(func() {})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotContainsAny(func() {})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 	})
 }
 
@@ -1209,52 +1204,52 @@ func TestArray_ContainsOnly(t *testing.T) {
 		value := NewArray(reporter, []interface{}{123, "foo"})
 
 		value.ContainsOnly(123)
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotContainsOnly(123)
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.ContainsOnly("foo")
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotContainsOnly("foo")
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.ContainsOnly(123, "foo", "foo")
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotContainsOnly(123, "foo", "foo")
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.ContainsOnly(123, "foo")
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotContainsOnly(123, "foo")
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.ContainsOnly("foo", 123)
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotContainsOnly("foo", 123)
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.ContainsOnly("foo", 123, "bar")
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotContainsOnly("foo", 123, "bar")
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 	})
 
 	t.Run("with duplicates", func(t *testing.T) {
@@ -1263,36 +1258,36 @@ func TestArray_ContainsOnly(t *testing.T) {
 		value := NewArray(reporter, []interface{}{123, "foo", "foo"})
 
 		value.ContainsOnly(123, "foo")
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotContainsOnly(123, "foo")
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.ContainsOnly(123, 123, "foo")
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotContainsOnly(123, 123, "foo")
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.ContainsOnly(123, "foo", "foo")
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotContainsOnly(123, "foo", "foo")
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.ContainsOnly("foo", 123, "foo")
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotContainsOnly("foo", 123, "foo")
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 	})
 
 	t.Run("canonization", func(t *testing.T) {
@@ -1305,20 +1300,20 @@ func TestArray_ContainsOnly(t *testing.T) {
 		value := NewArray(reporter, []interface{}{123, 456, 456})
 
 		value.ContainsOnly(456.0)
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotContainsOnly(456.0)
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.ContainsOnly(myInt(123), 456.0)
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotContainsOnly(myInt(123), 456.0)
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 	})
 
 	t.Run("invalid argument", func(t *testing.T) {
@@ -1327,12 +1322,12 @@ func TestArray_ContainsOnly(t *testing.T) {
 		value := NewArray(reporter, []interface{}{})
 
 		value.ContainsOnly(func() {})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotContainsOnly(func() {})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 	})
 }
 
@@ -1349,36 +1344,36 @@ func TestArray_HasValue(t *testing.T) {
 		})
 
 		array.HasValue(0, 123)
-		array.chain.assertNotFailed(t)
-		array.chain.clearFailed()
+		array.chain.assert(t, success)
+		array.chain.clear()
 
 		array.NotHasValue(0, 123)
-		array.chain.assertFailed(t)
-		array.chain.clearFailed()
+		array.chain.assert(t, failure)
+		array.chain.clear()
 
 		array.HasValue(1, []interface{}{"456", 789})
-		array.chain.assertNotFailed(t)
-		array.chain.clearFailed()
+		array.chain.assert(t, success)
+		array.chain.clear()
 
 		array.NotHasValue(1, []interface{}{"456", 789})
-		array.chain.assertFailed(t)
-		array.chain.clearFailed()
+		array.chain.assert(t, failure)
+		array.chain.clear()
 
 		array.HasValue(2, map[string]interface{}{"a": "b"})
-		array.chain.assertNotFailed(t)
-		array.chain.clearFailed()
+		array.chain.assert(t, success)
+		array.chain.clear()
 
 		array.NotHasValue(2, map[string]interface{}{"a": "b"})
-		array.chain.assertFailed(t)
-		array.chain.clearFailed()
+		array.chain.assert(t, failure)
+		array.chain.clear()
 
 		array.HasValue(3, 777)
-		array.chain.assertFailed(t)
-		array.chain.clearFailed()
+		array.chain.assert(t, failure)
+		array.chain.clear()
 
 		array.NotHasValue(3, 777)
-		array.chain.assertFailed(t)
-		array.chain.clearFailed()
+		array.chain.assert(t, failure)
+		array.chain.clear()
 	})
 
 	t.Run("struct", func(t *testing.T) {
@@ -1412,20 +1407,20 @@ func TestArray_HasValue(t *testing.T) {
 		}
 
 		array.HasValue(0, baz)
-		array.chain.assertNotFailed(t)
-		array.chain.clearFailed()
+		array.chain.assert(t, success)
+		array.chain.clear()
 
 		array.NotHasValue(0, baz)
-		array.chain.assertFailed(t)
-		array.chain.clearFailed()
+		array.chain.assert(t, failure)
+		array.chain.clear()
 
 		array.HasValue(0, Baz{})
-		array.chain.assertFailed(t)
-		array.chain.clearFailed()
+		array.chain.assert(t, failure)
+		array.chain.clear()
 
 		array.NotHasValue(0, Baz{})
-		array.chain.assertNotFailed(t)
-		array.chain.clearFailed()
+		array.chain.assert(t, success)
+		array.chain.clear()
 	})
 
 	t.Run("canonization", func(t *testing.T) {
@@ -1446,20 +1441,20 @@ func TestArray_HasValue(t *testing.T) {
 		})
 
 		array.HasValue(1, myArray{"456", myInt(789)})
-		array.chain.assertNotFailed(t)
-		array.chain.clearFailed()
+		array.chain.assert(t, success)
+		array.chain.clear()
 
 		array.NotHasValue(1, myArray{"456", myInt(789)})
-		array.chain.assertFailed(t)
-		array.chain.clearFailed()
+		array.chain.assert(t, failure)
+		array.chain.clear()
 
 		array.HasValue(2, myMap{"a": "b"})
-		array.chain.assertNotFailed(t)
-		array.chain.clearFailed()
+		array.chain.assert(t, success)
+		array.chain.clear()
 
 		array.NotHasValue(2, myMap{"a": "b"})
-		array.chain.assertFailed(t)
-		array.chain.clearFailed()
+		array.chain.assert(t, failure)
+		array.chain.clear()
 	})
 
 	t.Run("invalid index", func(t *testing.T) {
@@ -1468,12 +1463,12 @@ func TestArray_HasValue(t *testing.T) {
 		array := NewArray(reporter, []interface{}{1, 2, 3})
 
 		array.HasValue(-1, 999)
-		array.chain.assertFailed(t)
-		array.chain.clearFailed()
+		array.chain.assert(t, failure)
+		array.chain.clear()
 
 		array.NotHasValue(-1, 999)
-		array.chain.assertFailed(t)
-		array.chain.clearFailed()
+		array.chain.assert(t, failure)
+		array.chain.clear()
 	})
 
 	t.Run("invalid argument", func(t *testing.T) {
@@ -1482,12 +1477,12 @@ func TestArray_HasValue(t *testing.T) {
 		array := NewArray(reporter, []interface{}{1, 2, 3})
 
 		array.HasValue(1, func() {})
-		array.chain.assertFailed(t)
-		array.chain.clearFailed()
+		array.chain.assert(t, failure)
+		array.chain.clear()
 
 		array.NotHasValue(1, func() {})
-		array.chain.assertFailed(t)
-		array.chain.clearFailed()
+		array.chain.assert(t, failure)
+		array.chain.clear()
 	})
 }
 
@@ -1505,7 +1500,7 @@ func TestArray_Every(t *testing.T) {
 		})
 
 		assert.Equal(t, 3, invoked)
-		array.chain.assertNotFailed(t)
+		array.chain.assert(t, success)
 	})
 
 	t.Run("check index", func(t *testing.T) {
@@ -1521,7 +1516,7 @@ func TestArray_Every(t *testing.T) {
 		})
 
 		assert.Equal(t, 3, invoked)
-		array.chain.assertNotFailed(t)
+		array.chain.assert(t, success)
 	})
 
 	t.Run("empty array", func(t *testing.T) {
@@ -1534,7 +1529,7 @@ func TestArray_Every(t *testing.T) {
 		})
 
 		assert.Equal(t, 0, invoked)
-		array.chain.assertNotFailed(t)
+		array.chain.assert(t, success)
 	})
 
 	t.Run("one assertion fails", func(t *testing.T) {
@@ -1548,7 +1543,7 @@ func TestArray_Every(t *testing.T) {
 		})
 
 		assert.Equal(t, 3, invoked)
-		array.chain.assertFailed(t)
+		array.chain.assert(t, failure)
 	})
 
 	t.Run("all assertions fail", func(t *testing.T) {
@@ -1562,14 +1557,14 @@ func TestArray_Every(t *testing.T) {
 		})
 
 		assert.Equal(t, 3, invoked)
-		array.chain.assertFailed(t)
+		array.chain.assert(t, failure)
 	})
 
 	t.Run("invalid argument", func(t *testing.T) {
 		reporter := newMockReporter(t)
 		array := NewArray(reporter, []interface{}{1, 2, 3})
 		array.Every((func(index int, value *Value))(nil))
-		array.chain.assertFailed(t)
+		array.chain.assert(t, failure)
 	})
 }
 
@@ -1586,7 +1581,7 @@ func TestArray_Transform(t *testing.T) {
 		})
 
 		assert.Equal(t, []interface{}{float64(1), float64(2), float64(3)}, newArray.value)
-		newArray.chain.assertNotFailed(t)
+		newArray.chain.assert(t, success)
 	})
 
 	t.Run("transform value", func(t *testing.T) {
@@ -1602,7 +1597,7 @@ func TestArray_Transform(t *testing.T) {
 		})
 
 		assert.Equal(t, []interface{}{float64(4), float64(16), float64(36)}, newArray.value)
-		newArray.chain.assertNotFailed(t)
+		newArray.chain.assert(t, success)
 	})
 
 	t.Run("empty array", func(t *testing.T) {
@@ -1614,7 +1609,7 @@ func TestArray_Transform(t *testing.T) {
 			return nil
 		})
 
-		newArray.chain.assertNotFailed(t)
+		newArray.chain.assert(t, success)
 	})
 
 	t.Run("invalid argument", func(t *testing.T) {
@@ -1623,7 +1618,7 @@ func TestArray_Transform(t *testing.T) {
 
 		newArray := array.Transform(nil)
 
-		newArray.chain.assertFailed(t)
+		newArray.chain.assert(t, failure)
 	})
 
 	t.Run("canonization", func(t *testing.T) {
@@ -1643,7 +1638,7 @@ func TestArray_Transform(t *testing.T) {
 		})
 
 		assert.Equal(t, []interface{}{2.0, 4.0, 6.0}, newArray.Raw())
-		newArray.chain.assertNotFailed(t)
+		newArray.chain.assert(t, success)
 	})
 }
 
@@ -1659,8 +1654,8 @@ func TestArray_Filter(t *testing.T) {
 		assert.Equal(t, []interface{}{1.0, 3.0, 4.0, 6.0}, filteredArray.Raw())
 		assert.Equal(t, array.Raw(), []interface{}{1.0, 2.0, 3.0, 4.0, 5.0, 6.0})
 
-		array.chain.assertNotFailed(t)
-		filteredArray.chain.assertNotFailed(t)
+		array.chain.assert(t, success)
+		filteredArray.chain.assert(t, success)
 	})
 
 	t.Run("elements of different types", func(t *testing.T) {
@@ -1674,8 +1669,8 @@ func TestArray_Filter(t *testing.T) {
 		assert.Equal(t, []interface{}{"foo", true, 1.0}, filteredArray.Raw())
 		assert.Equal(t, array.Raw(), []interface{}{"foo", "bar", true, 1.0})
 
-		array.chain.assertNotFailed(t)
-		filteredArray.chain.assertNotFailed(t)
+		array.chain.assert(t, success)
+		filteredArray.chain.assert(t, success)
 	})
 
 	t.Run("empty array", func(t *testing.T) {
@@ -1689,8 +1684,8 @@ func TestArray_Filter(t *testing.T) {
 		assert.Equal(t, []interface{}{}, filteredArray.Raw())
 		assert.Equal(t, array.Raw(), []interface{}{})
 
-		array.chain.assertNotFailed(t)
-		filteredArray.chain.assertNotFailed(t)
+		array.chain.assert(t, success)
+		filteredArray.chain.assert(t, success)
 	})
 
 	t.Run("no match", func(t *testing.T) {
@@ -1704,8 +1699,8 @@ func TestArray_Filter(t *testing.T) {
 		assert.Equal(t, []interface{}{}, filteredArray.Raw())
 		assert.Equal(t, array.Raw(), []interface{}{"foo", "bar", true, 1.0})
 
-		array.chain.assertNotFailed(t)
-		filteredArray.chain.assertNotFailed(t)
+		array.chain.assert(t, success)
+		filteredArray.chain.assert(t, success)
 	})
 
 	t.Run("assertion fails", func(t *testing.T) {
@@ -1720,16 +1715,16 @@ func TestArray_Filter(t *testing.T) {
 		assert.Equal(t, []interface{}{"foo", "baz"}, filteredArray.Raw())
 		assert.Equal(t, array.Raw(), []interface{}{1.0, "foo", "bar", 4.0, "baz", 6.0})
 
-		array.chain.assertNotFailed(t)
-		filteredArray.chain.assertNotFailed(t)
+		array.chain.assert(t, success)
+		filteredArray.chain.assert(t, success)
 	})
 
 	t.Run("invalid argument", func(t *testing.T) {
 		reporter := newMockReporter(t)
 		array := NewArray(reporter, []interface{}{"foo", "bar", true, 1.0})
 		filteredArray := array.Filter((func(index int, value *Value) bool)(nil))
-		array.chain.assertFailed(t)
-		filteredArray.chain.assertFailed(t)
+		array.chain.assert(t, failure)
+		filteredArray.chain.assert(t, failure)
 	})
 }
 
@@ -1745,8 +1740,8 @@ func TestArray_Find(t *testing.T) {
 		assert.Equal(t, 2.0, foundValue.Raw())
 		assert.Equal(t, array.Raw(), []interface{}{1.0, 2.0, 3.0, 4.0, 5.0, 6.0})
 
-		array.chain.assertNotFailed(t)
-		foundValue.chain.assertNotFailed(t)
+		array.chain.assert(t, success)
+		foundValue.chain.assert(t, success)
 	})
 
 	t.Run("elements of different types", func(t *testing.T) {
@@ -1761,8 +1756,8 @@ func TestArray_Find(t *testing.T) {
 		assert.Equal(t, "bar", foundValue.Raw())
 		assert.Equal(t, array.Raw(), []interface{}{1.0, "foo", true, "bar"})
 
-		array.chain.assertNotFailed(t)
-		foundValue.chain.assertNotFailed(t)
+		array.chain.assert(t, success)
+		foundValue.chain.assert(t, success)
 	})
 
 	t.Run("first match", func(t *testing.T) {
@@ -1777,8 +1772,8 @@ func TestArray_Find(t *testing.T) {
 		assert.Equal(t, "foo", foundValue.Raw())
 		assert.Equal(t, array.Raw(), []interface{}{1.0, "foo", true, "bar"})
 
-		array.chain.assertNotFailed(t)
-		foundValue.chain.assertNotFailed(t)
+		array.chain.assert(t, success)
+		foundValue.chain.assert(t, success)
 	})
 
 	t.Run("no match", func(t *testing.T) {
@@ -1792,8 +1787,8 @@ func TestArray_Find(t *testing.T) {
 		assert.Equal(t, nil, foundValue.Raw())
 		assert.Equal(t, array.Raw(), []interface{}{1.0, "foo", true, "bar"})
 
-		array.chain.assertFailed(t)
-		foundValue.chain.assertFailed(t)
+		array.chain.assert(t, failure)
+		foundValue.chain.assert(t, failure)
 	})
 
 	t.Run("empty array", func(t *testing.T) {
@@ -1807,8 +1802,8 @@ func TestArray_Find(t *testing.T) {
 		assert.Equal(t, nil, foundValue.Raw())
 		assert.Equal(t, array.Raw(), []interface{}{})
 
-		array.chain.assertFailed(t)
-		foundValue.chain.assertFailed(t)
+		array.chain.assert(t, failure)
+		foundValue.chain.assert(t, failure)
 	})
 
 	t.Run("predicate returns true, assertion fails, no match", func(t *testing.T) {
@@ -1823,8 +1818,8 @@ func TestArray_Find(t *testing.T) {
 		assert.Equal(t, nil, foundValue.Raw())
 		assert.Equal(t, array.Raw(), []interface{}{1.0, 2.0})
 
-		array.chain.assertFailed(t)
-		foundValue.chain.assertFailed(t)
+		array.chain.assert(t, failure)
+		foundValue.chain.assert(t, failure)
 	})
 
 	t.Run("predicate returns true, assertion fails, have match", func(t *testing.T) {
@@ -1839,8 +1834,8 @@ func TestArray_Find(t *testing.T) {
 		assert.Equal(t, "str", foundValue.Raw())
 		assert.Equal(t, array.Raw(), []interface{}{1.0, 2.0, "str"})
 
-		array.chain.assertNotFailed(t)
-		foundValue.chain.assertNotFailed(t)
+		array.chain.assert(t, success)
+		foundValue.chain.assert(t, success)
 	})
 
 	t.Run("invalid argument", func(t *testing.T) {
@@ -1852,8 +1847,8 @@ func TestArray_Find(t *testing.T) {
 		assert.Equal(t, nil, foundValue.Raw())
 		assert.Equal(t, array.Raw(), []interface{}{1.0, 2.0})
 
-		array.chain.assertFailed(t)
-		foundValue.chain.assertFailed(t)
+		array.chain.assert(t, failure)
+		foundValue.chain.assert(t, failure)
 	})
 }
 
@@ -1874,9 +1869,9 @@ func TestArray_FindAll(t *testing.T) {
 		assert.Equal(t, []interface{}{2.0, 5.0}, actual)
 		assert.Equal(t, array.Raw(), []interface{}{1.0, 2.0, 3.0, 4.0, 5.0, 6.0})
 
-		array.chain.assertNotFailed(t)
+		array.chain.assert(t, success)
 		for _, value := range foundValues {
-			value.chain.assertNotFailed(t)
+			value.chain.assert(t, success)
 		}
 	})
 
@@ -1897,9 +1892,9 @@ func TestArray_FindAll(t *testing.T) {
 		assert.Equal(t, []interface{}{"foo", "bar"}, actual)
 		assert.Equal(t, array.Raw(), []interface{}{1.0, "foo", true, "bar"})
 
-		array.chain.assertNotFailed(t)
+		array.chain.assert(t, success)
 		for _, value := range foundValues {
-			value.chain.assertNotFailed(t)
+			value.chain.assert(t, success)
 		}
 	})
 
@@ -1919,9 +1914,9 @@ func TestArray_FindAll(t *testing.T) {
 		assert.Equal(t, []interface{}{}, actual)
 		assert.Equal(t, array.Raw(), []interface{}{1.0, "foo", true, "bar"})
 
-		array.chain.assertNotFailed(t)
+		array.chain.assert(t, success)
 		for _, value := range foundValues {
-			value.chain.assertNotFailed(t)
+			value.chain.assert(t, success)
 		}
 	})
 
@@ -1941,9 +1936,9 @@ func TestArray_FindAll(t *testing.T) {
 		assert.Equal(t, []interface{}{}, actual)
 		assert.Equal(t, array.Raw(), []interface{}{})
 
-		array.chain.assertNotFailed(t)
+		array.chain.assert(t, success)
 		for _, value := range foundValues {
-			value.chain.assertNotFailed(t)
+			value.chain.assert(t, success)
 		}
 	})
 
@@ -1964,9 +1959,9 @@ func TestArray_FindAll(t *testing.T) {
 		assert.Equal(t, []interface{}{}, actual)
 		assert.Equal(t, array.Raw(), []interface{}{1.0, 2.0})
 
-		array.chain.assertNotFailed(t)
+		array.chain.assert(t, success)
 		for _, value := range foundValues {
-			value.chain.assertNotFailed(t)
+			value.chain.assert(t, success)
 		}
 	})
 
@@ -1987,9 +1982,9 @@ func TestArray_FindAll(t *testing.T) {
 		assert.Equal(t, []interface{}{"foo", "bar"}, actual)
 		assert.Equal(t, array.Raw(), []interface{}{"foo", 1.0, 2.0, "bar"})
 
-		array.chain.assertNotFailed(t)
+		array.chain.assert(t, success)
 		for _, value := range foundValues {
-			value.chain.assertNotFailed(t)
+			value.chain.assert(t, success)
 		}
 	})
 
@@ -2007,9 +2002,9 @@ func TestArray_FindAll(t *testing.T) {
 		assert.Equal(t, []interface{}{}, actual)
 		assert.Equal(t, array.Raw(), []interface{}{1.0, 2.0})
 
-		array.chain.assertFailed(t)
+		array.chain.assert(t, failure)
 		for _, value := range foundValues {
-			value.chain.assertFailed(t)
+			value.chain.assert(t, failure)
 		}
 	})
 }
@@ -2024,7 +2019,7 @@ func TestArray_NotFind(t *testing.T) {
 		})
 
 		assert.Same(t, array, afterArray)
-		array.chain.assertNotFailed(t)
+		array.chain.assert(t, success)
 	})
 
 	t.Run("have match", func(t *testing.T) {
@@ -2036,7 +2031,7 @@ func TestArray_NotFind(t *testing.T) {
 		})
 
 		assert.Same(t, array, afterArray)
-		array.chain.assertFailed(t)
+		array.chain.assert(t, failure)
 	})
 
 	t.Run("empty array", func(t *testing.T) {
@@ -2048,7 +2043,7 @@ func TestArray_NotFind(t *testing.T) {
 		})
 
 		assert.Same(t, array, afterArray)
-		array.chain.assertNotFailed(t)
+		array.chain.assert(t, success)
 	})
 
 	t.Run("predicate returns true, assertion fails, no match", func(t *testing.T) {
@@ -2061,7 +2056,7 @@ func TestArray_NotFind(t *testing.T) {
 		})
 
 		assert.Same(t, array, afterArray)
-		array.chain.assertNotFailed(t)
+		array.chain.assert(t, success)
 	})
 
 	t.Run("predicate returns true, assertion fails, have match", func(t *testing.T) {
@@ -2074,7 +2069,7 @@ func TestArray_NotFind(t *testing.T) {
 		})
 
 		assert.Same(t, array, afterArray)
-		array.chain.assertFailed(t)
+		array.chain.assert(t, failure)
 	})
 
 	t.Run("invalid argument", func(t *testing.T) {
@@ -2084,7 +2079,7 @@ func TestArray_NotFind(t *testing.T) {
 		afterArray := array.NotFind(nil)
 
 		assert.Same(t, array, afterArray)
-		array.chain.assertFailed(t)
+		array.chain.assert(t, failure)
 	})
 }
 
@@ -2321,10 +2316,10 @@ func TestArray_IsOrdered(t *testing.T) {
 			if tc.isInvalid {
 				t.Run("normal", func(t *testing.T) {
 					NewArray(reporter, tc.args.values).IsOrdered(tc.args.less...).
-						chain.assertFailed(t)
+						chain.assert(t, failure)
 
 					NewArray(reporter, tc.args.values).NotOrdered(tc.args.less...).
-						chain.assertFailed(t)
+						chain.assert(t, failure)
 				})
 
 				t.Run("reversed", func(t *testing.T) {
@@ -2334,29 +2329,29 @@ func TestArray_IsOrdered(t *testing.T) {
 					})
 
 					NewArray(reporter, tc.args.values).IsOrdered(tc.args.less...).
-						chain.assertFailed(t)
+						chain.assert(t, failure)
 
 					NewArray(reporter, tc.args.values).NotOrdered(tc.args.less...).
-						chain.assertFailed(t)
+						chain.assert(t, failure)
 				})
 			} else {
 				t.Run("is ordered", func(t *testing.T) {
 					if tc.isOrdered {
 						NewArray(reporter, tc.args.values).IsOrdered(tc.args.less...).
-							chain.assertNotFailed(t)
+							chain.assert(t, success)
 					} else {
 						NewArray(reporter, tc.args.values).IsOrdered(tc.args.less...).
-							chain.assertFailed(t)
+							chain.assert(t, failure)
 					}
 				})
 
 				t.Run("not ordered", func(t *testing.T) {
 					if tc.isUnordered {
 						NewArray(reporter, tc.args.values).NotOrdered(tc.args.less...).
-							chain.assertNotFailed(t)
+							chain.assert(t, success)
 					} else {
 						NewArray(reporter, tc.args.values).NotOrdered(tc.args.less...).
-							chain.assertFailed(t)
+							chain.assert(t, failure)
 					}
 				})
 			}
@@ -2371,7 +2366,7 @@ func TestArray_ComparatorErrors(t *testing.T) {
 		fn := builtinComparator(chain, nil)
 
 		assert.Nil(t, fn)
-		chain.assertNotFailed(t)
+		chain.assert(t, success)
 	})
 
 	t.Run("0 elements", func(t *testing.T) {
@@ -2380,7 +2375,7 @@ func TestArray_ComparatorErrors(t *testing.T) {
 		fn := builtinComparator(chain, []interface{}{})
 
 		assert.Nil(t, fn)
-		chain.assertNotFailed(t)
+		chain.assert(t, success)
 	})
 
 	t.Run("1 element", func(t *testing.T) {
@@ -2391,7 +2386,7 @@ func TestArray_ComparatorErrors(t *testing.T) {
 		})
 
 		assert.Nil(t, fn)
-		chain.assertNotFailed(t)
+		chain.assert(t, success)
 	})
 
 	t.Run("2 elements, bad_type", func(t *testing.T) {
@@ -2403,7 +2398,7 @@ func TestArray_ComparatorErrors(t *testing.T) {
 		})
 
 		assert.Nil(t, fn)
-		chain.assertFailed(t)
+		chain.assert(t, failure)
 	})
 
 	t.Run("2 elements, good types", func(t *testing.T) {
@@ -2415,6 +2410,6 @@ func TestArray_ComparatorErrors(t *testing.T) {
 		})
 
 		assert.NotNil(t, fn)
-		chain.assertNotFailed(t)
+		chain.assert(t, success)
 	})
 }
diff --git a/boolean_test.go b/boolean_test.go
index 7b2fc4e..588b572 100644
--- a/boolean_test.go
+++ b/boolean_test.go
@@ -1,19 +1,19 @@
 package httpexpect
 
 import (
+	"fmt"
 	"testing"
 
 	"github.com/stretchr/testify/assert"
 )
 
 func TestBoolean_FailedChain(t *testing.T) {
-	chain := newMockChain(t)
-	chain.setFailed()
+	chain := newFailedChain(t)
 
 	value := newBoolean(chain, false)
-	value.chain.assertFailed(t)
+	value.chain.assert(t, failure)
 
-	value.Path("$").chain.assertFailed(t)
+	value.Path("$").chain.assert(t, failure)
 	value.Schema("")
 	value.Alias("foo")
 
@@ -33,7 +33,7 @@ func TestBoolean_Constructors(t *testing.T) {
 		reporter := newMockReporter(t)
 		value := NewBoolean(reporter, true)
 		value.IsEqual(true)
-		value.chain.assertNotFailed(t)
+		value.chain.assert(t, success)
 	})
 
 	t.Run("config", func(t *testing.T) {
@@ -42,7 +42,7 @@ func TestBoolean_Constructors(t *testing.T) {
 			Reporter: reporter,
 		}, true)
 		value.IsEqual(true)
-		value.chain.assertNotFailed(t)
+		value.chain.assert(t, success)
 	})
 
 	t.Run("chain", func(t *testing.T) {
@@ -62,7 +62,7 @@ func TestBoolean_Decode(t *testing.T) {
 		var target interface{}
 		value.Decode(&target)
 
-		value.chain.assertNotFailed(t)
+		value.chain.assert(t, success)
 		assert.Equal(t, true, target)
 	})
 
@@ -74,7 +74,7 @@ func TestBoolean_Decode(t *testing.T) {
 		var target bool
 		value.Decode(&target)
 
-		value.chain.assertNotFailed(t)
+		value.chain.assert(t, success)
 		assert.Equal(t, true, target)
 	})
 
@@ -85,7 +85,7 @@ func TestBoolean_Decode(t *testing.T) {
 
 		value.Decode(nil)
 
-		value.chain.assertFailed(t)
+		value.chain.assert(t, failure)
 	})
 
 	t.Run("target is unmarshable", func(t *testing.T) {
@@ -95,7 +95,7 @@ func TestBoolean_Decode(t *testing.T) {
 
 		value.Decode(123)
 
-		value.chain.assertFailed(t)
+		value.chain.assert(t, failure)
 	})
 }
 
@@ -117,112 +117,100 @@ func TestBoolean_Getters(t *testing.T) {
 	value := NewBoolean(reporter, true)
 
 	assert.Equal(t, true, value.Raw())
-	value.chain.assertNotFailed(t)
-	value.chain.clearFailed()
+	value.chain.assert(t, success)
+	value.chain.clear()
 
 	assert.Equal(t, true, value.Path("$").Raw())
-	value.chain.assertNotFailed(t)
-	value.chain.clearFailed()
+	value.chain.assert(t, success)
+	value.chain.clear()
 
 	value.Schema(`{"type": "boolean"}`)
-	value.chain.assertNotFailed(t)
-	value.chain.clearFailed()
+	value.chain.assert(t, success)
+	value.chain.clear()
 
 	value.Schema(`{"type": "object"}`)
-	value.chain.assertFailed(t)
-	value.chain.clearFailed()
+	value.chain.assert(t, failure)
+	value.chain.clear()
 }
 
-func TestBoolean_True(t *testing.T) {
-	reporter := newMockReporter(t)
-
-	value := NewBoolean(reporter, true)
-
-	assert.Equal(t, true, value.Raw())
-
-	value.IsEqual(true)
-	value.chain.assertNotFailed(t)
-	value.chain.clearFailed()
-
-	value.IsEqual(false)
-	value.chain.assertFailed(t)
-	value.chain.clearFailed()
+func TestBoolean_IsEqual(t *testing.T) {
+	for _, value := range []bool{true, false} {
+		t.Run(fmt.Sprintf("%v", value), func(t *testing.T) {
+			reporter := newMockReporter(t)
 
-	value.NotEqual(false)
-	value.chain.assertNotFailed(t)
-	value.chain.clearFailed()
+			NewBoolean(reporter, value).IsEqual(value).
+				chain.assert(t, success)
 
-	value.NotEqual(true)
-	value.chain.assertFailed(t)
-	value.chain.clearFailed()
+			NewBoolean(reporter, value).IsEqual(!value).
+				chain.assert(t, failure)
 
-	value.IsTrue()
-	value.chain.assertNotFailed(t)
-	value.chain.clearFailed()
+			NewBoolean(reporter, value).NotEqual(value).
+				chain.assert(t, failure)
 
-	value.IsFalse()
-	value.chain.assertFailed(t)
-	value.chain.clearFailed()
-
-	value.InList(true, true)
-	value.chain.assertNotFailed(t)
-	value.chain.clearFailed()
-
-	value.NotInList(true, false)
-	value.chain.assertFailed(t)
-	value.chain.clearFailed()
+			NewBoolean(reporter, value).NotEqual(!value).
+				chain.assert(t, success)
+		})
+	}
 }
 
-func TestBoolean_False(t *testing.T) {
-	reporter := newMockReporter(t)
-
-	value := NewBoolean(reporter, false)
-
-	assert.Equal(t, false, value.Raw())
-
-	value.IsEqual(true)
-	value.chain.assertFailed(t)
-	value.chain.clearFailed()
-
-	value.IsEqual(false)
-	value.chain.assertNotFailed(t)
-	value.chain.clearFailed()
+func TestBoolean_IsValue(t *testing.T) {
+	for _, value := range []bool{true, false} {
+		t.Run(fmt.Sprintf("%v", value), func(t *testing.T) {
+			reporter := newMockReporter(t)
+
+			if value {
+				NewBoolean(reporter, value).IsTrue().
+					chain.assert(t, success)
+
+				NewBoolean(reporter, value).IsFalse().
+					chain.assert(t, failure)
+			} else {
+				NewBoolean(reporter, value).IsTrue().
+					chain.assert(t, failure)
+
+				NewBoolean(reporter, value).IsFalse().
+					chain.assert(t, success)
+			}
+		})
+	}
+}
 
-	value.NotEqual(false)
-	value.chain.assertFailed(t)
-	value.chain.clearFailed()
+func TestBoolean_InList(t *testing.T) {
+	t.Run("basic", func(t *testing.T) {
+		for _, value := range []bool{true, false} {
+			t.Run(fmt.Sprintf("%v", value), func(t *testing.T) {
+				reporter := newMockReporter(t)
 
-	value.NotEqual(true)
-	value.chain.assertNotFailed(t)
-	value.chain.clearFailed()
+				NewBoolean(reporter, value).InList(value).
+					chain.assert(t, success)
 
-	value.IsTrue()
-	value.chain.assertFailed(t)
-	value.chain.clearFailed()
+				NewBoolean(reporter, value).InList(!value, value).
+					chain.assert(t, success)
 
-	value.IsFalse()
-	value.chain.assertNotFailed(t)
-	value.chain.clearFailed()
+				NewBoolean(reporter, value).InList(!value, !value).
+					chain.assert(t, failure)
 
-	value.InList(true, true)
-	value.chain.assertFailed(t)
-	value.chain.clearFailed()
+				NewBoolean(reporter, value).NotInList(value).
+					chain.assert(t, failure)
 
-	value.NotInList(true, true)
-	value.chain.assertNotFailed(t)
-	value.chain.clearFailed()
-}
+				NewBoolean(reporter, value).NotInList(!value, value).
+					chain.assert(t, failure)
 
-func TestBoolean_Usage(t *testing.T) {
-	reporter := newMockReporter(t)
+				NewBoolean(reporter, value).NotInList(!value, !value).
+					chain.assert(t, success)
+			})
+		}
+	})
 
-	value := NewBoolean(reporter, true)
+	t.Run("invalid argument", func(t *testing.T) {
+		for _, value := range []bool{true, false} {
+			reporter := newMockReporter(t)
 
-	value.InList()
-	value.chain.assertFailed(t)
-	value.chain.clearFailed()
+			NewBoolean(reporter, value).InList().
+				chain.assert(t, failure)
 
-	value.NotInList()
-	value.chain.assertFailed(t)
-	value.chain.clearFailed()
+			NewBoolean(reporter, value).NotInList().
+				chain.assert(t, failure)
+		}
+	})
 }
diff --git a/cookie_test.go b/cookie_test.go
index 270e1b9..a115386 100644
--- a/cookie_test.go
+++ b/cookie_test.go
@@ -11,7 +11,7 @@ import (
 
 func TestCookie_FailedChain(t *testing.T) {
 	check := func(value *Cookie, isNil bool) {
-		value.chain.assertFailed(t)
+		value.chain.assert(t, failure)
 
 		if isNil {
 			assert.Nil(t, value.Raw())
@@ -21,21 +21,19 @@ func TestCookie_FailedChain(t *testing.T) {
 
 		value.Alias("foo")
 
-		value.Name().chain.assertFailed(t)
-		value.Value().chain.assertFailed(t)
-		value.Domain().chain.assertFailed(t)
-		value.Path().chain.assertFailed(t)
-		value.Expires().chain.assertFailed(t)
-		value.MaxAge().chain.assertFailed(t)
+		value.Name().chain.assert(t, failure)
+		value.Value().chain.assert(t, failure)
+		value.Domain().chain.assert(t, failure)
+		value.Path().chain.assert(t, failure)
+		value.Expires().chain.assert(t, failure)
+		value.MaxAge().chain.assert(t, failure)
 
 		value.HasMaxAge()
 		value.NotHasMaxAge()
 	}
 
 	t.Run("failed chain", func(t *testing.T) {
-		chain := newMockChain(t)
-		chain.setFailed()
-
+		chain := newFailedChain(t)
 		value := newCookie(chain, &http.Cookie{})
 
 		check(value, false)
@@ -43,16 +41,13 @@ func TestCookie_FailedChain(t *testing.T) {
 
 	t.Run("nil value", func(t *testing.T) {
 		chain := newMockChain(t)
-
 		value := newCookie(chain, nil)
 
 		check(value, true)
 	})
 
 	t.Run("failed chain, nil value", func(t *testing.T) {
-		chain := newMockChain(t)
-		chain.setFailed()
-
+		chain := newFailedChain(t)
 		value := newCookie(chain, nil)
 
 		check(value, true)
@@ -77,7 +72,7 @@ func TestCookie_Constructors(t *testing.T) {
 		value.Domain().IsEqual("example.com")
 		value.Expires().IsEqual(time.Unix(1234, 0))
 		value.MaxAge().IsEqual(123 * time.Second)
-		value.chain.assertNotFailed(t)
+		value.chain.assert(t, success)
 	})
 
 	t.Run("config", func(t *testing.T) {
@@ -90,7 +85,7 @@ func TestCookie_Constructors(t *testing.T) {
 		value.Domain().IsEqual("example.com")
 		value.Expires().IsEqual(time.Unix(1234, 0))
 		value.MaxAge().IsEqual(123 * time.Second)
-		value.chain.assertNotFailed(t)
+		value.chain.assert(t, success)
 	})
 
 	t.Run("chain", func(t *testing.T) {
@@ -131,14 +126,14 @@ func TestCookie_Getters(t *testing.T) {
 		MaxAge:  123,
 	})
 
-	value.chain.assertNotFailed(t)
+	value.chain.assert(t, success)
 
-	value.Name().chain.assertNotFailed(t)
-	value.Value().chain.assertNotFailed(t)
-	value.Domain().chain.assertNotFailed(t)
-	value.Path().chain.assertNotFailed(t)
-	value.Expires().chain.assertNotFailed(t)
-	value.MaxAge().chain.assertNotFailed(t)
+	value.Name().chain.assert(t, success)
+	value.Value().chain.assert(t, success)
+	value.Domain().chain.assert(t, success)
+	value.Path().chain.assert(t, success)
+	value.Expires().chain.assert(t, success)
+	value.MaxAge().chain.assert(t, success)
 
 	assert.Equal(t, "name", value.Name().Raw())
 	assert.Equal(t, "value", value.Value().Raw())
@@ -147,68 +142,58 @@ func TestCookie_Getters(t *testing.T) {
 	assert.True(t, time.Unix(1234, 0).Equal(value.Expires().Raw()))
 	assert.Equal(t, 123*time.Second, value.MaxAge().Raw())
 
-	value.chain.assertNotFailed(t)
+	value.chain.assert(t, success)
 }
 
 func TestCookie_MaxAge(t *testing.T) {
-	reporter := newMockReporter(t)
-
-	t.Run("unset", func(t *testing.T) {
-		value := NewCookie(reporter, &http.Cookie{
-			MaxAge: 0,
-		})
-
-		value.chain.assertNotFailed(t)
-
-		value.HasMaxAge().chain.assertFailed(t)
-		value.chain.clearFailed()
-
-		value.NotHasMaxAge().chain.assertNotFailed(t)
-		value.chain.clearFailed()
-
-		require.Nil(t, value.MaxAge().value)
-
-		value.MaxAge().NotSet().chain.assertNotFailed(t)
-		value.MaxAge().IsSet().chain.assertFailed(t)
-	})
-
-	t.Run("zero", func(t *testing.T) {
-		value := NewCookie(reporter, &http.Cookie{
-			MaxAge: -1,
-		})
-
-		value.chain.assertNotFailed(t)
-
-		value.HasMaxAge().chain.assertNotFailed(t)
-		value.chain.clearFailed()
-
-		value.NotHasMaxAge().chain.assertFailed(t)
-		value.chain.clearFailed()
-
-		require.NotNil(t, value.MaxAge().value)
-		require.Equal(t, time.Duration(0), *value.MaxAge().value)
-
-		value.MaxAge().IsSet().chain.assertNotFailed(t)
-		value.MaxAge().IsEqual(0).chain.assertNotFailed(t)
-	})
+	cases := []struct {
+		name          string
+		maxAge        int
+		wantHasMaxAge chainResult
+		wantDuration  time.Duration
+	}{
+		{
+			name:          "unset",
+			maxAge:        0,
+			wantHasMaxAge: failure,
+		},
+		{
+			name:          "zero",
+			maxAge:        -1,
+			wantHasMaxAge: success,
+			wantDuration:  time.Duration(0),
+		},
+		{
+			name:          "non-zero",
+			maxAge:        3,
+			wantHasMaxAge: success,
+			wantDuration:  time.Duration(3) * time.Second,
+		},
+	}
 
-	t.Run("non-zero", func(t *testing.T) {
-		value := NewCookie(reporter, &http.Cookie{
-			MaxAge: 3,
+	for _, tc := range cases {
+		t.Run(tc.name, func(t *testing.T) {
+			reporter := newMockReporter(t)
+			data := &http.Cookie{
+				MaxAge: tc.maxAge,
+			}
+
+			NewCookie(reporter, data).HasMaxAge().
+				chain.assert(t, tc.wantHasMaxAge)
+
+			NewCookie(reporter, data).NotHasMaxAge().
+				chain.assert(t, !tc.wantHasMaxAge)
+
+			if tc.wantHasMaxAge {
+				require.NotNil(t,
+					NewCookie(reporter, data).MaxAge().value)
+
+				require.Equal(t, tc.wantDuration,
+					*NewCookie(reporter, data).MaxAge().value)
+			} else {
+				require.Nil(t,
+					NewCookie(reporter, data).MaxAge().value)
+			}
 		})
-
-		value.chain.assertNotFailed(t)
-
-		value.HasMaxAge().chain.assertNotFailed(t)
-		value.chain.clearFailed()
-
-		value.NotHasMaxAge().chain.assertFailed(t)
-		value.chain.clearFailed()
-
-		require.NotNil(t, value.MaxAge().value)
-		require.Equal(t, 3*time.Second, *value.MaxAge().value)
-
-		value.MaxAge().IsSet().chain.assertNotFailed(t)
-		value.MaxAge().IsEqual(3 * time.Second).chain.assertNotFailed(t)
-	})
+	}
 }
diff --git a/datetime_test.go b/datetime_test.go
index 1ed6e10..cc9ff17 100644
--- a/datetime_test.go
+++ b/datetime_test.go
@@ -8,12 +8,11 @@ import (
 )
 
 func TestDateTime_FailedChain(t *testing.T) {
-	chain := newMockChain(t)
-	chain.setFailed()
+	chain := newFailedChain(t)
 
 	tm := time.Unix(0, 0)
 	value := newDateTime(chain, tm)
-	value.chain.assertFailed(t)
+	value.chain.assert(t, failure)
 
 	value.Alias("foo")
 
@@ -28,19 +27,19 @@ func TestDateTime_FailedChain(t *testing.T) {
 	value.Lt(tm)
 	value.Le(tm)
 
-	value.Zone().chain.assertFailed(t)
-	value.Year().chain.assertFailed(t)
-	value.Month().chain.assertFailed(t)
-	value.Day().chain.assertFailed(t)
-	value.WeekDay().chain.assertFailed(t)
-	value.YearDay().chain.assertFailed(t)
-	value.Hour().chain.assertFailed(t)
-	value.Minute().chain.assertFailed(t)
-	value.Second().chain.assertFailed(t)
-	value.Nanosecond().chain.assertFailed(t)
-
-	value.AsUTC().chain.assertFailed(t)
-	value.AsLocal().chain.assertFailed(t)
+	value.Zone().chain.assert(t, failure)
+	value.Year().chain.assert(t, failure)
+	value.Month().chain.assert(t, failure)
+	value.Day().chain.assert(t, failure)
+	value.WeekDay().chain.assert(t, failure)
+	value.YearDay().chain.assert(t, failure)
+	value.Hour().chain.assert(t, failure)
+	value.Minute().chain.assert(t, failure)
+	value.Second().chain.assert(t, failure)
+	value.Nanosecond().chain.assert(t, failure)
+
+	value.AsUTC().chain.assert(t, failure)
+	value.AsLocal().chain.assert(t, failure)
 }
 
 func TestDateTime_Constructors(t *testing.T) {
@@ -50,7 +49,7 @@ func TestDateTime_Constructors(t *testing.T) {
 		reporter := newMockReporter(t)
 		value := NewDateTime(reporter, time)
 		value.IsEqual(time)
-		value.chain.assertNotFailed(t)
+		value.chain.assert(t, success)
 	})
 
 	t.Run("config", func(t *testing.T) {
@@ -59,7 +58,7 @@ func TestDateTime_Constructors(t *testing.T) {
 			Reporter: reporter,
 		}, time)
 		value.IsEqual(time)
-		value.chain.assertNotFailed(t)
+		value.chain.assert(t, success)
 	})
 
 	t.Run("chain", func(t *testing.T) {
@@ -89,20 +88,20 @@ func TestDateTime_Getters(t *testing.T) {
 
 	value := NewDateTime(reporter, parsedTime)
 
-	value.chain.assertNotFailed(t)
-
-	value.Zone().chain.assertNotFailed(t)
-	value.Year().chain.assertNotFailed(t)
-	value.Month().chain.assertNotFailed(t)
-	value.Day().chain.assertNotFailed(t)
-	value.WeekDay().chain.assertNotFailed(t)
-	value.YearDay().chain.assertNotFailed(t)
-	value.Hour().chain.assertNotFailed(t)
-	value.Minute().chain.assertNotFailed(t)
-	value.Second().chain.assertNotFailed(t)
-	value.Nanosecond().chain.assertNotFailed(t)
-	value.AsUTC().chain.assertNotFailed(t)
-	value.AsLocal().chain.assertNotFailed(t)
+	value.chain.assert(t, success)
+
+	value.Zone().chain.assert(t, success)
+	value.Year().chain.assert(t, success)
+	value.Month().chain.assert(t, success)
+	value.Day().chain.assert(t, success)
+	value.WeekDay().chain.assert(t, success)
+	value.YearDay().chain.assert(t, success)
+	value.Hour().chain.assert(t, success)
+	value.Minute().chain.assert(t, success)
+	value.Second().chain.assert(t, success)
+	value.Nanosecond().chain.assert(t, success)
+	value.AsUTC().chain.assert(t, success)
+	value.AsLocal().chain.assert(t, success)
 
 	expectedTime := parsedTime
 	expectedZone, _ := expectedTime.Zone()
@@ -127,20 +126,20 @@ func TestDateTime_IsEqual(t *testing.T) {
 	assert.True(t, time.Unix(0, 1234).Equal(value.Raw()))
 
 	value.IsEqual(time.Unix(0, 1234))
-	value.chain.assertNotFailed(t)
-	value.chain.clearFailed()
+	value.chain.assert(t, success)
+	value.chain.clear()
 
 	value.IsEqual(time.Unix(0, 4321))
-	value.chain.assertFailed(t)
-	value.chain.clearFailed()
+	value.chain.assert(t, failure)
+	value.chain.clear()
 
 	value.NotEqual(time.Unix(0, 4321))
-	value.chain.assertNotFailed(t)
-	value.chain.clearFailed()
+	value.chain.assert(t, success)
+	value.chain.clear()
 
 	value.NotEqual(time.Unix(0, 1234))
-	value.chain.assertFailed(t)
-	value.chain.clearFailed()
+	value.chain.assert(t, failure)
+	value.chain.clear()
 }
 
 func TestDateTime_IsGreater(t *testing.T) {
@@ -149,24 +148,24 @@ func TestDateTime_IsGreater(t *testing.T) {
 	value := NewDateTime(reporter, time.Unix(0, 1234))
 
 	value.Gt(time.Unix(0, 1234-1))
-	value.chain.assertNotFailed(t)
-	value.chain.clearFailed()
+	value.chain.assert(t, success)
+	value.chain.clear()
 
 	value.Gt(time.Unix(0, 1234))
-	value.chain.assertFailed(t)
-	value.chain.clearFailed()
+	value.chain.assert(t, failure)
+	value.chain.clear()
 
 	value.Ge(time.Unix(0, 1234-1))
-	value.chain.assertNotFailed(t)
-	value.chain.clearFailed()
+	value.chain.assert(t, success)
+	value.chain.clear()
 
 	value.Ge(time.Unix(0, 1234))
-	value.chain.assertNotFailed(t)
-	value.chain.clearFailed()
+	value.chain.assert(t, success)
+	value.chain.clear()
 
 	value.Ge(time.Unix(0, 1234+1))
-	value.chain.assertFailed(t)
-	value.chain.clearFailed()
+	value.chain.assert(t, failure)
+	value.chain.clear()
 }
 
 func TestDateTime_IsLesser(t *testing.T) {
@@ -175,82 +174,82 @@ func TestDateTime_IsLesser(t *testing.T) {
 	value := NewDateTime(reporter, time.Unix(0, 1234))
 
 	value.Lt(time.Unix(0, 1234+1))
-	value.chain.assertNotFailed(t)
-	value.chain.clearFailed()
+	value.chain.assert(t, success)
+	value.chain.clear()
 
 	value.Lt(time.Unix(0, 1234))
-	value.chain.assertFailed(t)
-	value.chain.clearFailed()
+	value.chain.assert(t, failure)
+	value.chain.clear()
 
 	value.Le(time.Unix(0, 1234+1))
-	value.chain.assertNotFailed(t)
-	value.chain.clearFailed()
+	value.chain.assert(t, success)
+	value.chain.clear()
 
 	value.Le(time.Unix(0, 1234))
-	value.chain.assertNotFailed(t)
-	value.chain.clearFailed()
+	value.chain.assert(t, success)
+	value.chain.clear()
 
 	value.Le(time.Unix(0, 1234-1))
-	value.chain.assertFailed(t)
-	value.chain.clearFailed()
+	value.chain.assert(t, failure)
+	value.chain.clear()
 }
 
 func TestDateTime_InRange(t *testing.T) {
 	cases := []struct {
-		name             string
-		value            time.Time
-		min              time.Time
-		max              time.Time
-		expectInRange    bool
-		expectNotInRange bool
+		name           string
+		value          time.Time
+		min            time.Time
+		max            time.Time
+		wantInRange    chainResult
+		wantNotInRange chainResult
 	}{
 		{
-			name:             "value equal to both min and max",
-			value:            time.Unix(0, 1234),
-			min:              time.Unix(0, 1234),
-			max:              time.Unix(0, 1234),
-			expectInRange:    true,
-			expectNotInRange: false,
+			name:           "value equal to both min and max",
+			value:          time.Unix(0, 1234),
+			min:            time.Unix(0, 1234),
+			max:            time.Unix(0, 1234),
+			wantInRange:    success,
+			wantNotInRange: failure,
 		},
 		{
-			name:             "value after min and equal to max",
-			value:            time.Unix(0, 1234),
-			min:              time.Unix(0, 1234-1),
-			max:              time.Unix(0, 1234),
-			expectInRange:    true,
-			expectNotInRange: false,
+			name:           "value after min and equal to max",
+			value:          time.Unix(0, 1234),
+			min:            time.Unix(0, 1234-1),
+			max:            time.Unix(0, 1234),
+			wantInRange:    success,
+			wantNotInRange: failure,
 		},
 		{
-			name:             "value equal to min and before max",
-			value:            time.Unix(0, 1234),
-			min:              time.Unix(0, 1234),
-			max:              time.Unix(0, 1234+1),
-			expectInRange:    true,
-			expectNotInRange: false,
+			name:           "value equal to min and before max",
+			value:          time.Unix(0, 1234),
+			min:            time.Unix(0, 1234),
+			max:            time.Unix(0, 1234+1),
+			wantInRange:    success,
+			wantNotInRange: failure,
 		},
 		{
-			name:             "value before range",
-			value:            time.Unix(0, 1234),
-			min:              time.Unix(0, 1234+1),
-			max:              time.Unix(0, 1234+2),
-			expectInRange:    false,
-			expectNotInRange: true,
+			name:           "value before range",
+			value:          time.Unix(0, 1234),
+			min:            time.Unix(0, 1234+1),
+			max:            time.Unix(0, 1234+2),
+			wantInRange:    failure,
+			wantNotInRange: success,
 		},
 		{
-			name:             "value after range",
-			value:            time.Unix(0, 1234),
-			min:              time.Unix(0, 1234-2),
-			max:              time.Unix(0, 1234-1),
-			expectInRange:    false,
-			expectNotInRange: true,
+			name:           "value after range",
+			value:          time.Unix(0, 1234),
+			min:            time.Unix(0, 1234-2),
+			max:            time.Unix(0, 1234-1),
+			wantInRange:    failure,
+			wantNotInRange: success,
 		},
 		{
-			name:             "invalid range",
-			value:            time.Unix(0, 1234),
-			min:              time.Unix(0, 1234+1),
-			max:              time.Unix(0, 1234-1),
-			expectInRange:    false,
-			expectNotInRange: true,
+			name:           "invalid range",
+			value:          time.Unix(0, 1234),
+			min:            time.Unix(0, 1234+1),
+			max:            time.Unix(0, 1234-1),
+			wantInRange:    failure,
+			wantNotInRange: success,
 		},
 	}
 
@@ -258,56 +257,43 @@ func TestDateTime_InRange(t *testing.T) {
 		t.Run(tc.name, func(t *testing.T) {
 			reporter := newMockReporter(t)
 
-			if tc.expectInRange {
-				NewDateTime(reporter, tc.value).
-					InRange(tc.min, tc.max).
-					chain.assertNotFailed(t)
-			} else {
-				NewDateTime(reporter, tc.value).
-					InRange(tc.min, tc.max).
-					chain.assertFailed(t)
-			}
-			if tc.expectNotInRange {
-				NewDateTime(reporter, tc.value).
-					NotInRange(tc.min, tc.max).
-					chain.assertNotFailed(t)
-			} else {
-				NewDateTime(reporter, tc.value).
-					NotInRange(tc.min, tc.max).
-					chain.assertFailed(t)
-			}
+			NewDateTime(reporter, tc.value).InRange(tc.min, tc.max).
+				chain.assert(t, tc.wantInRange)
+
+			NewDateTime(reporter, tc.value).NotInRange(tc.min, tc.max).
+				chain.assert(t, tc.wantNotInRange)
 		})
 	}
 }
 
 func TestDateTime_InList(t *testing.T) {
 	cases := []struct {
-		name            string
-		value           time.Time
-		list            []time.Time
-		expectInList    bool
-		expectNotInList bool
+		name          string
+		value         time.Time
+		list          []time.Time
+		wantInList    chainResult
+		wantNotInList chainResult
 	}{
 		{
-			name:            "empty list",
-			value:           time.Unix(0, 1234),
-			list:            []time.Time{},
-			expectInList:    false,
-			expectNotInList: false,
+			name:          "empty list",
+			value:         time.Unix(0, 1234),
+			list:          []time.Time{},
+			wantInList:    failure,
+			wantNotInList: failure,
 		},
 		{
-			name:            "value present in list",
-			value:           time.Unix(0, 1234),
-			list:            []time.Time{time.Unix(0, 1234), time.Unix(0, 1234+1)},
-			expectInList:    true,
-			expectNotInList: false,
+			name:          "value present in list",
+			value:         time.Unix(0, 1234),
+			list:          []time.Time{time.Unix(0, 1234), time.Unix(0, 1234+1)},
+			wantInList:    success,
+			wantNotInList: failure,
 		},
 		{
-			name:            "value not present in list",
-			value:           time.Unix(0, 1234),
-			list:            []time.Time{time.Unix(0, 1234-1), time.Unix(0, 1234+1)},
-			expectInList:    false,
-			expectNotInList: true,
+			name:          "value not present in list",
+			value:         time.Unix(0, 1234),
+			list:          []time.Time{time.Unix(0, 1234-1), time.Unix(0, 1234+1)},
+			wantInList:    failure,
+			wantNotInList: success,
 		},
 	}
 
@@ -315,24 +301,11 @@ func TestDateTime_InList(t *testing.T) {
 		t.Run(tc.name, func(t *testing.T) {
 			reporter := newMockReporter(t)
 
-			if tc.expectInList {
-				NewDateTime(reporter, tc.value).
-					InList(tc.list...).
-					chain.assertNotFailed(t)
-			} else {
-				NewDateTime(reporter, tc.value).
-					InList(tc.list...).
-					chain.assertFailed(t)
-			}
-			if tc.expectNotInList {
-				NewDateTime(reporter, tc.value).
-					NotInList(tc.list...).
-					chain.assertNotFailed(t)
-			} else {
-				NewDateTime(reporter, tc.value).
-					NotInList(tc.list...).
-					chain.assertFailed(t)
-			}
+			NewDateTime(reporter, tc.value).InList(tc.list...).
+				chain.assert(t, tc.wantInList)
+
+			NewDateTime(reporter, tc.value).NotInList(tc.list...).
+				chain.assert(t, tc.wantNotInList)
 		})
 	}
 }
diff --git a/duration_test.go b/duration_test.go
index 35db222..4867f30 100644
--- a/duration_test.go
+++ b/duration_test.go
@@ -8,12 +8,11 @@ import (
 )
 
 func TestDuration_FailedChain(t *testing.T) {
-	chain := newMockChain(t)
-	chain.setFailed()
+	chain := newFailedChain(t)
 
 	tm := time.Second
 	value := newDuration(chain, &tm)
-	value.chain.assertFailed(t)
+	value.chain.assert(t, failure)
 
 	value.Alias("foo")
 	value.IsEqual(tm)
@@ -35,7 +34,7 @@ func TestDuration_Constructors(t *testing.T) {
 		reporter := newMockReporter(t)
 		value := NewDuration(reporter, tm)
 		value.IsEqual(tm)
-		value.chain.assertNotFailed(t)
+		value.chain.assert(t, success)
 	})
 
 	t.Run("config", func(t *testing.T) {
@@ -44,7 +43,7 @@ func TestDuration_Constructors(t *testing.T) {
 			Reporter: reporter,
 		}, tm)
 		value.IsEqual(tm)
-		value.chain.assertNotFailed(t)
+		value.chain.assert(t, success)
 	})
 
 	t.Run("chain", func(t *testing.T) {
@@ -53,7 +52,6 @@ func TestDuration_Constructors(t *testing.T) {
 		assert.NotSame(t, value.chain, chain)
 		assert.Equal(t, value.chain.context.Path, chain.context.Path)
 	})
-
 }
 
 func TestDuration_Alias(t *testing.T) {
@@ -68,35 +66,6 @@ func TestDuration_Alias(t *testing.T) {
 	assert.Equal(t, []string{"foo"}, value.chain.context.AliasedPath)
 }
 
-func TestDuration_Set(t *testing.T) {
-	chain := newMockChain(t)
-
-	tm := time.Second
-	value := newDuration(chain, &tm)
-
-	value.IsSet()
-	value.chain.assertNotFailed(t)
-	value.chain.clearFailed()
-
-	value.NotSet()
-	value.chain.assertFailed(t)
-	value.chain.clearFailed()
-}
-
-func TestDuration_Unset(t *testing.T) {
-	chain := newMockChain(t)
-
-	value := newDuration(chain, nil)
-
-	value.IsSet()
-	value.chain.assertFailed(t)
-	value.chain.clearFailed()
-
-	value.NotSet()
-	value.chain.assertNotFailed(t)
-	value.chain.clearFailed()
-}
-
 func TestDuration_IsEqual(t *testing.T) {
 	reporter := newMockReporter(t)
 
@@ -105,20 +74,20 @@ func TestDuration_IsEqual(t *testing.T) {
 	assert.Equal(t, time.Second, value.Raw())
 
 	value.IsEqual(time.Second)
-	value.chain.assertNotFailed(t)
-	value.chain.clearFailed()
+	value.chain.assert(t, success)
+	value.chain.clear()
 
 	value.IsEqual(time.Minute)
-	value.chain.assertFailed(t)
-	value.chain.clearFailed()
+	value.chain.assert(t, failure)
+	value.chain.clear()
 
 	value.NotEqual(time.Minute)
-	value.chain.assertNotFailed(t)
-	value.chain.clearFailed()
+	value.chain.assert(t, success)
+	value.chain.clear()
 
 	value.NotEqual(time.Second)
-	value.chain.assertFailed(t)
-	value.chain.clearFailed()
+	value.chain.assert(t, failure)
+	value.chain.clear()
 }
 
 func TestDuration_IsGreater(t *testing.T) {
@@ -127,24 +96,24 @@ func TestDuration_IsGreater(t *testing.T) {
 	value := NewDuration(reporter, time.Second)
 
 	value.Gt(time.Second - 1)
-	value.chain.assertNotFailed(t)
-	value.chain.clearFailed()
+	value.chain.assert(t, success)
+	value.chain.clear()
 
 	value.Gt(time.Second)
-	value.chain.assertFailed(t)
-	value.chain.clearFailed()
+	value.chain.assert(t, failure)
+	value.chain.clear()
 
 	value.Ge(time.Second - 1)
-	value.chain.assertNotFailed(t)
-	value.chain.clearFailed()
+	value.chain.assert(t, success)
+	value.chain.clear()
 
 	value.Ge(time.Second)
-	value.chain.assertNotFailed(t)
-	value.chain.clearFailed()
+	value.chain.assert(t, success)
+	value.chain.clear()
 
 	value.Ge(time.Second + 1)
-	value.chain.assertFailed(t)
-	value.chain.clearFailed()
+	value.chain.assert(t, failure)
+	value.chain.clear()
 }
 
 func TestDuration_IsLesser(t *testing.T) {
@@ -153,82 +122,82 @@ func TestDuration_IsLesser(t *testing.T) {
 	value := NewDuration(reporter, time.Second)
 
 	value.Lt(time.Second + 1)
-	value.chain.assertNotFailed(t)
-	value.chain.clearFailed()
+	value.chain.assert(t, success)
+	value.chain.clear()
 
 	value.Lt(time.Second)
-	value.chain.assertFailed(t)
-	value.chain.clearFailed()
+	value.chain.assert(t, failure)
+	value.chain.clear()
 
 	value.Le(time.Second + 1)
-	value.chain.assertNotFailed(t)
-	value.chain.clearFailed()
+	value.chain.assert(t, success)
+	value.chain.clear()
 
 	value.Le(time.Second)
-	value.chain.assertNotFailed(t)
-	value.chain.clearFailed()
+	value.chain.assert(t, success)
+	value.chain.clear()
 
 	value.Le(time.Second - 1)
-	value.chain.assertFailed(t)
-	value.chain.clearFailed()
+	value.chain.assert(t, failure)
+	value.chain.clear()
 }
 
 func TestDuration_InRange(t *testing.T) {
 	cases := []struct {
-		name             string
-		value            time.Duration
-		min              time.Duration
-		max              time.Duration
-		expectInRange    bool
-		expectNotInRange bool
+		name           string
+		value          time.Duration
+		min            time.Duration
+		max            time.Duration
+		wantInRange    chainResult
+		wantNotInRange chainResult
 	}{
 		{
-			name:             "value equal to both min and max",
-			value:            time.Second,
-			min:              time.Second,
-			max:              time.Second,
-			expectInRange:    true,
-			expectNotInRange: false,
+			name:           "value equal to both min and max",
+			value:          time.Second,
+			min:            time.Second,
+			max:            time.Second,
+			wantInRange:    success,
+			wantNotInRange: failure,
 		},
 		{
-			name:             "value greater than min and equal to max",
-			value:            time.Second,
-			min:              time.Second - 1,
-			max:              time.Second,
-			expectInRange:    true,
-			expectNotInRange: false,
+			name:           "value greater than min and equal to max",
+			value:          time.Second,
+			min:            time.Second - 1,
+			max:            time.Second,
+			wantInRange:    success,
+			wantNotInRange: failure,
 		},
 		{
-			name:             "value equal to min and smaller than max",
-			value:            time.Second,
-			min:              time.Second,
-			max:              time.Second + 1,
-			expectInRange:    true,
-			expectNotInRange: false,
+			name:           "value equal to min and smaller than max",
+			value:          time.Second,
+			min:            time.Second,
+			max:            time.Second + 1,
+			wantInRange:    success,
+			wantNotInRange: failure,
 		},
 		{
-			name:             "value smaller than min",
-			value:            time.Second,
-			min:              time.Second + 1,
-			max:              time.Second + 2,
-			expectInRange:    false,
-			expectNotInRange: true,
+			name:           "value smaller than min",
+			value:          time.Second,
+			min:            time.Second + 1,
+			max:            time.Second + 2,
+			wantInRange:    failure,
+			wantNotInRange: success,
 		},
 		{
-			name:             "value greater than max",
-			value:            time.Second,
-			min:              time.Second - 2,
-			max:              time.Second - 1,
-			expectInRange:    false,
-			expectNotInRange: true,
+			name:           "value greater than max",
+			value:          time.Second,
+			min:            time.Second - 2,
+			max:            time.Second - 1,
+			wantInRange:    failure,
+			wantNotInRange: success,
 		},
 		{
-			name:             "min smaller than max",
-			value:            time.Second,
-			min:              time.Second + 1,
-			max:              time.Second - 1,
-			expectInRange:    false,
-			expectNotInRange: true,
+			name:           "min smaller than max",
+			value:          time.Second,
+			min:            time.Second + 1,
+			max:            time.Second - 1,
+			wantInRange:    failure,
+			wantNotInRange: success,
 		},
 	}
 
@@ -236,56 +205,43 @@ func TestDuration_InRange(t *testing.T) {
 		t.Run(tc.name, func(t *testing.T) {
 			reporter := newMockReporter(t)
 
-			if tc.expectInRange {
-				NewDuration(reporter, tc.value).
-					InRange(tc.min, tc.max).
-					chain.assertNotFailed(t)
-			} else {
-				NewDuration(reporter, tc.value).
-					InRange(tc.min, tc.max).
-					chain.assertFailed(t)
-			}
-			if tc.expectNotInRange {
-				NewDuration(reporter, tc.value).
-					NotInRange(tc.min, tc.max).
-					chain.assertNotFailed(t)
-			} else {
-				NewDuration(reporter, tc.value).
-					NotInRange(tc.min, tc.max).
-					chain.assertFailed(t)
-			}
+			NewDuration(reporter, tc.value).InRange(tc.min, tc.max).
+				chain.assert(t, tc.wantInRange)
+
+			NewDuration(reporter, tc.value).NotInRange(tc.min, tc.max).
+				chain.assert(t, tc.wantNotInRange)
 		})
 	}
 }
 
 func TestDuration_InList(t *testing.T) {
 	cases := []struct {
-		name            string
-		value           time.Duration
-		list            []time.Duration
-		expectInList    bool
-		expectNotInList bool
+		name          string
+		value         time.Duration
+		list          []time.Duration
+		wantInList    chainResult
+		wantNotInList chainResult
 	}{
 		{
-			name:            "empty list",
-			value:           time.Second,
-			list:            []time.Duration{},
-			expectInList:    false,
-			expectNotInList: false,
+			name:          "empty list",
+			value:         time.Second,
+			list:          []time.Duration{},
+			wantInList:    failure,
+			wantNotInList: failure,
 		},
 		{
-			name:            "value present in list",
-			value:           time.Second,
-			list:            []time.Duration{time.Second, time.Minute},
-			expectInList:    true,
-			expectNotInList: false,
+			name:          "value present in list",
+			value:         time.Second,
+			list:          []time.Duration{time.Second, time.Minute},
+			wantInList:    success,
+			wantNotInList: failure,
 		},
 		{
-			name:            "value not present in list",
-			value:           time.Second,
-			list:            []time.Duration{time.Second - 1, time.Second + 1},
-			expectInList:    false,
-			expectNotInList: true,
+			name:          "value not present in list",
+			value:         time.Second,
+			list:          []time.Duration{time.Second - 1, time.Second + 1},
+			wantInList:    failure,
+			wantNotInList: success,
 		},
 	}
 
@@ -293,24 +249,11 @@ func TestDuration_InList(t *testing.T) {
 		t.Run(tc.name, func(t *testing.T) {
 			reporter := newMockReporter(t)
 
-			if tc.expectInList {
-				NewDuration(reporter, tc.value).
-					InList(tc.list...).
-					chain.assertNotFailed(t)
-			} else {
-				NewDuration(reporter, tc.value).
-					InList(tc.list...).
-					chain.assertFailed(t)
-			}
-			if tc.expectNotInList {
-				NewDuration(reporter, tc.value).
-					NotInList(tc.list...).
-					chain.assertNotFailed(t)
-			} else {
-				NewDuration(reporter, tc.value).
-					NotInList(tc.list...).
-					chain.assertFailed(t)
-			}
+			NewDuration(reporter, tc.value).InList(tc.list...).
+				chain.assert(t, tc.wantInList)
+
+			NewDuration(reporter, tc.value).NotInList(tc.list...).
+				chain.assert(t, tc.wantNotInList)
 		})
 	}
 }
diff --git a/environment_test.go b/environment_test.go
index 383af3f..ea865d7 100644
--- a/environment_test.go
+++ b/environment_test.go
@@ -13,7 +13,7 @@ func TestEnvironment_Constructors(t *testing.T) {
 	t.Run("reporter", func(t *testing.T) {
 		reporter := newMockReporter(t)
 		env := NewEnvironment(reporter)
-		env.chain.assertNotFailed(t)
+		env.chain.assert(t, success)
 	})
 
 	t.Run("config", func(t *testing.T) {
@@ -21,7 +21,7 @@ func TestEnvironment_Constructors(t *testing.T) {
 		env := NewEnvironmentC(Config{
 			Reporter: reporter,
 		})
-		env.chain.assertNotFailed(t)
+		env.chain.assert(t, success)
 	})
 
 	t.Run("chain", func(t *testing.T) {
@@ -44,7 +44,7 @@ func TestEnvironment_Reentrancy(t *testing.T) {
 	}
 
 	env.Get("bad_key")
-	env.chain.assertFailed(t)
+	env.chain.assert(t, failure)
 
 	assert.True(t, reportCalled)
 }
@@ -53,41 +53,41 @@ func TestEnvironment_Basic(t *testing.T) {
 	env := newEnvironment(newMockChain(t))
 
 	assert.False(t, env.Has("good_key"))
-	env.chain.assertNotFailed(t)
+	env.chain.assert(t, success)
 
 	env.Put("good_key", 123)
-	env.chain.assertNotFailed(t)
+	env.chain.assert(t, success)
 
 	assert.True(t, env.Has("good_key"))
 	assert.NotNil(t, env.Get("good_key"))
 	assert.Equal(t, 123, env.Get("good_key").(int))
-	env.chain.assertNotFailed(t)
+	env.chain.assert(t, success)
 
 	assert.False(t, env.Has("bad_key"))
-	env.chain.assertNotFailed(t)
+	env.chain.assert(t, success)
 
 	assert.Nil(t, env.Get("bad_key"))
-	env.chain.assertFailed(t)
+	env.chain.assert(t, failure)
 }
 
 func TestEnvironment_Delete(t *testing.T) {
 	env := newEnvironment(newMockChain(t))
 
 	env.Put("good_key", 123)
-	env.chain.assertNotFailed(t)
+	env.chain.assert(t, success)
 
 	assert.True(t, env.Has("good_key"))
 	assert.NotNil(t, env.Get("good_key"))
 	assert.Equal(t, 123, env.Get("good_key").(int))
-	env.chain.assertNotFailed(t)
+	env.chain.assert(t, success)
 
 	env.Delete("good_key")
-	env.chain.assertNotFailed(t)
+	env.chain.assert(t, success)
 
 	assert.False(t, env.Has("good_key"))
 	assert.Nil(t, env.Get("good_key"))
-	env.chain.assertFailed(t)
-	env.chain.clearFailed()
+	env.chain.assert(t, failure)
+	env.chain.clear()
 }
 
 func TestEnvironment_Clear(t *testing.T) {
@@ -96,62 +96,62 @@ func TestEnvironment_Clear(t *testing.T) {
 	for i := 1; i < 11; i++ {
 		key := fmt.Sprint("key", i)
 		env.Put(key, i)
-		env.chain.assertNotFailed(t)
+		env.chain.assert(t, success)
 		assert.True(t, env.Has(key))
 		assert.NotNil(t, env.Get(key))
 		assert.Equal(t, i, env.Get(key).(int))
-		env.chain.assertNotFailed(t)
+		env.chain.assert(t, success)
 	}
 
 	env.Clear()
-	env.chain.assertNotFailed(t)
+	env.chain.assert(t, success)
 
 	for i := 1; i < 11; i++ {
 		key := fmt.Sprint("key", i)
 		assert.False(t, env.Has(key))
 		assert.Nil(t, env.Get(key))
-		env.chain.assertFailed(t)
-		env.chain.clearFailed()
+		env.chain.assert(t, failure)
+		env.chain.clear()
 	}
 
 	assert.Zero(t, len(env.data))
-	env.chain.assertNotFailed(t)
+	env.chain.assert(t, success)
 }
 
 func TestEnvironment_NotFound(t *testing.T) {
 	env := newEnvironment(newMockChain(t))
 
 	assert.Nil(t, env.Get("bad_key"))
-	env.chain.assertFailed(t)
-	env.chain.clearFailed()
+	env.chain.assert(t, failure)
+	env.chain.clear()
 
 	assert.Equal(t, false, env.GetBool("bad_key"))
-	env.chain.assertFailed(t)
-	env.chain.clearFailed()
+	env.chain.assert(t, failure)
+	env.chain.clear()
 
 	assert.Equal(t, 0, env.GetInt("bad_key"))
-	env.chain.assertFailed(t)
-	env.chain.clearFailed()
+	env.chain.assert(t, failure)
+	env.chain.clear()
 
 	assert.Equal(t, 0.0, env.GetFloat("bad_key"))
-	env.chain.assertFailed(t)
-	env.chain.clearFailed()
+	env.chain.assert(t, failure)
+	env.chain.clear()
 
 	assert.Equal(t, "", env.GetString("bad_key"))
-	env.chain.assertFailed(t)
-	env.chain.clearFailed()
+	env.chain.assert(t, failure)
+	env.chain.clear()
 
 	assert.Nil(t, env.GetBytes("bad_key"))
-	env.chain.assertFailed(t)
-	env.chain.clearFailed()
+	env.chain.assert(t, failure)
+	env.chain.clear()
 
 	assert.Equal(t, time.Duration(0), env.GetDuration("bad_key"))
-	env.chain.assertFailed(t)
-	env.chain.clearFailed()
+	env.chain.assert(t, failure)
+	env.chain.clear()
 
 	assert.Equal(t, time.Unix(0, 0), env.GetTime("bad_key"))
-	env.chain.assertFailed(t)
-	env.chain.clearFailed()
+	env.chain.assert(t, failure)
+	env.chain.clear()
 }
 
 func TestEnvironment_Bool(t *testing.T) {
@@ -187,15 +187,15 @@ func TestEnvironment_Bool(t *testing.T) {
 				env := newEnvironment(newMockChain(t))
 
 				env.Put("key", tc.put)
-				env.chain.assertNotFailed(t)
+				env.chain.assert(t, success)
 
 				val := env.GetBool("key")
 				assert.Equal(t, tc.get, val)
 
 				if tc.ok {
-					env.chain.assertNotFailed(t)
+					env.chain.assert(t, success)
 				} else {
-					env.chain.assertFailed(t)
+					env.chain.assert(t, failure)
 				}
 			})
 	}
@@ -284,15 +284,15 @@ func TestEnvironment_Int(t *testing.T) {
 				env := newEnvironment(newMockChain(t))
 
 				env.Put("key", tc.put)
-				env.chain.assertNotFailed(t)
+				env.chain.assert(t, success)
 
 				val := env.GetInt("key")
 				assert.Equal(t, tc.get, val)
 
 				if tc.ok {
-					env.chain.assertNotFailed(t)
+					env.chain.assert(t, success)
 				} else {
-					env.chain.assertFailed(t)
+					env.chain.assert(t, failure)
 				}
 			})
 	}
@@ -336,15 +336,15 @@ func TestEnvironment_Float(t *testing.T) {
 				env := newEnvironment(newMockChain(t))
 
 				env.Put("key", tc.put)
-				env.chain.assertNotFailed(t)
+				env.chain.assert(t, success)
 
 				val := env.GetFloat("key")
 				assert.Equal(t, tc.get, val)
 
 				if tc.ok {
-					env.chain.assertNotFailed(t)
+					env.chain.assert(t, success)
 				} else {
-					env.chain.assertFailed(t)
+					env.chain.assert(t, failure)
 				}
 			})
 	}
@@ -378,15 +378,15 @@ func TestEnvironment_String(t *testing.T) {
 				env := newEnvironment(newMockChain(t))
 
 				env.Put("key", tc.put)
-				env.chain.assertNotFailed(t)
+				env.chain.assert(t, success)
 
 				val := env.GetString("key")
 				assert.Equal(t, tc.get, val)
 
 				if tc.ok {
-					env.chain.assertNotFailed(t)
+					env.chain.assert(t, success)
 				} else {
-					env.chain.assertFailed(t)
+					env.chain.assert(t, failure)
 				}
 			})
 	}
@@ -420,15 +420,15 @@ func TestEnvironment_Bytes(t *testing.T) {
 				env := newEnvironment(newMockChain(t))
 
 				env.Put("key", tc.put)
-				env.chain.assertNotFailed(t)
+				env.chain.assert(t, success)
 
 				val := env.GetBytes("key")
 				assert.Equal(t, tc.get, val)
 
 				if tc.ok {
-					env.chain.assertNotFailed(t)
+					env.chain.assert(t, success)
 				} else {
-					env.chain.assertFailed(t)
+					env.chain.assert(t, failure)
 				}
 			})
 	}
@@ -462,15 +462,15 @@ func TestEnvironment_Duration(t *testing.T) {
 				env := newEnvironment(newMockChain(t))
 
 				env.Put("key", tc.put)
-				env.chain.assertNotFailed(t)
+				env.chain.assert(t, success)
 
 				val := env.GetDuration("key")
 				assert.Equal(t, tc.get, val)
 
 				if tc.ok {
-					env.chain.assertNotFailed(t)
+					env.chain.assert(t, success)
 				} else {
-					env.chain.assertFailed(t)
+					env.chain.assert(t, failure)
 				}
 			})
 	}
@@ -504,15 +504,15 @@ func TestEnvironment_Time(t *testing.T) {
 				env := newEnvironment(newMockChain(t))
 
 				env.Put("key", tc.put)
-				env.chain.assertNotFailed(t)
+				env.chain.assert(t, success)
 
 				val := env.GetTime("key")
 				assert.Equal(t, tc.get, val)
 
 				if tc.ok {
-					env.chain.assertNotFailed(t)
+					env.chain.assert(t, success)
 				} else {
-					env.chain.assertFailed(t)
+					env.chain.assert(t, failure)
 				}
 			})
 	}
@@ -569,13 +569,13 @@ func TestEnvironment_Glob(t *testing.T) {
 	t.Run("invalid glob pattern", func(t *testing.T) {
 		env := newEnvironment(newMockChain(t))
 		assert.Equal(t, []string{}, env.Glob("k[1-2"))
-		env.chain.assertFailed(t)
-		env.chain.clearFailed()
+		env.chain.assert(t, failure)
+		env.chain.clear()
 
 		env.Put("k1", 1)
 		env.Put("k2", 2)
 		assert.Equal(t, []string{}, env.Glob("k[]"))
-		env.chain.assertFailed(t)
-		env.chain.clearFailed()
+		env.chain.assert(t, failure)
+		env.chain.clear()
 	})
 }
diff --git a/match_test.go b/match_test.go
index 061ad3a..2d74261 100644
--- a/match_test.go
+++ b/match_test.go
@@ -7,17 +7,16 @@ import (
 )
 
 func TestMatch_FailedChain(t *testing.T) {
-	chain := newMockChain(t)
-	chain.setFailed()
+	chain := newFailedChain(t)
 
 	value := newMatch(chain, nil, nil)
-	value.chain.assertFailed(t)
+	value.chain.assert(t, failure)
 
 	value.Alias("foo")
 
-	value.Length().chain.assertFailed(t)
-	value.Index(0).chain.assertFailed(t)
-	value.Name("").chain.assertFailed(t)
+	value.Length().chain.assert(t, failure)
+	value.Index(0).chain.assert(t, failure)
+	value.Name("").chain.assert(t, failure)
 
 	value.IsEmpty()
 	value.NotEmpty()
@@ -33,7 +32,7 @@ func TestMatch_Constructors(t *testing.T) {
 		reporter := newMockReporter(t)
 		value := NewMatch(reporter, matches, names)
 		assert.Equal(t, matches, value.Raw())
-		value.chain.assertNotFailed(t)
+		value.chain.assert(t, success)
 	})
 
 	t.Run("config", func(t *testing.T) {
@@ -42,7 +41,7 @@ func TestMatch_Constructors(t *testing.T) {
 			Reporter: reporter,
 		}, matches, names)
 		assert.Equal(t, matches, value.Raw())
-		value.chain.assertNotFailed(t)
+		value.chain.assert(t, success)
 	})
 
 	t.Run("chain", func(t *testing.T) {
@@ -87,49 +86,49 @@ func TestMatch_Getters(t *testing.T) {
 	assert.Equal(t, "m0", value.Index(0).Raw())
 	assert.Equal(t, "m1", value.Index(1).Raw())
 	assert.Equal(t, "m2", value.Index(2).Raw())
-	value.chain.assertNotFailed(t)
+	value.chain.assert(t, success)
 
 	assert.Equal(t, "m1", value.Name("n1").Raw())
 	assert.Equal(t, "m2", value.Name("n2").Raw())
-	value.chain.assertNotFailed(t)
+	value.chain.assert(t, success)
 
 	assert.Equal(t, "", value.Index(-1).Raw())
-	value.chain.assertFailed(t)
-	value.chain.clearFailed()
+	value.chain.assert(t, failure)
+	value.chain.clear()
 
 	assert.Equal(t, "", value.Index(3).Raw())
-	value.chain.assertFailed(t)
-	value.chain.clearFailed()
+	value.chain.assert(t, failure)
+	value.chain.clear()
 
 	assert.Equal(t, "", value.Name("").Raw())
-	value.chain.assertFailed(t)
-	value.chain.clearFailed()
+	value.chain.assert(t, failure)
+	value.chain.clear()
 
 	assert.Equal(t, "", value.Name("bad").Raw())
-	value.chain.assertFailed(t)
-	value.chain.clearFailed()
+	value.chain.assert(t, failure)
+	value.chain.clear()
 }
 
 func TestMatch_IsEmpty(t *testing.T) {
 	cases := []struct {
-		name        string
-		submatch    []string
-		expectEmpty bool
+		name      string
+		submatch  []string
+		wantEmpty chainResult
 	}{
 		{
-			name:        "string",
-			submatch:    []string{"m"},
-			expectEmpty: false,
+			name:      "string",
+			submatch:  []string{"m"},
+			wantEmpty: failure,
 		},
 		{
-			name:        "empty string slice",
-			submatch:    []string{},
-			expectEmpty: true,
+			name:      "empty string slice",
+			submatch:  []string{},
+			wantEmpty: success,
 		},
 		{
-			name:        "nil",
-			submatch:    nil,
-			expectEmpty: true,
+			name:      "nil",
+			submatch:  nil,
+			wantEmpty: success,
 		},
 	}
 
@@ -137,21 +136,15 @@ func TestMatch_IsEmpty(t *testing.T) {
 		t.Run(tc.name, func(t *testing.T) {
 			reporter := newMockReporter(t)
 
-			if tc.expectEmpty {
-				NewMatch(reporter, tc.submatch, nil).IsEmpty().
-					chain.assertNotFailed(t)
+			NewMatch(reporter, tc.submatch, nil).IsEmpty().
+				chain.assert(t, tc.wantEmpty)
 
-				NewMatch(reporter, tc.submatch, nil).NotEmpty().
-					chain.assertFailed(t)
+			NewMatch(reporter, tc.submatch, nil).NotEmpty().
+				chain.assert(t, !tc.wantEmpty)
 
+			if tc.wantEmpty {
 				assert.Equal(t, []string{},
 					NewMatch(reporter, tc.submatch, nil).Raw())
-			} else {
-				NewMatch(reporter, tc.submatch, nil).NotEmpty().
-					chain.assertNotFailed(t)
-
-				NewMatch(reporter, tc.submatch, nil).IsEmpty().
-					chain.assertFailed(t)
 			}
 		})
 	}
@@ -160,46 +153,46 @@ func TestMatch_IsEmpty(t *testing.T) {
 func TestMatch_Values(t *testing.T) {
 	type wantMatch struct {
 		target []string
-		fail   bool
+		result chainResult
 	}
 
 	cases := []struct {
-		name        string
-		submatches  []string
-		expectMatch []wantMatch
+		name       string
+		submatches []string
+		wantMatch  []wantMatch
 	}{
 		{
 			name:       "nil match instance",
 			submatches: nil,
-			expectMatch: []wantMatch{
-				{target: nil, fail: false},
-				{target: []string{""}, fail: true},
+			wantMatch: []wantMatch{
+				{target: nil, result: success},
+				{target: []string{""}, result: failure},
 			},
 		},
 		{
 			name:       "empty match instance",
 			submatches: []string{},
-			expectMatch: []wantMatch{
-				{target: nil, fail: false},
-				{target: []string{""}, fail: true},
+			wantMatch: []wantMatch{
+				{target: nil, result: success},
+				{target: []string{""}, result: failure},
 			},
 		},
 		{
 			name:       "not empty index 0 only",
 			submatches: []string{"m0"},
-			expectMatch: []wantMatch{
-				{target: nil, fail: false},
-				{target: []string{"m0"}, fail: true},
+			wantMatch: []wantMatch{
+				{target: nil, result: success},
+				{target: []string{"m0"}, result: failure},
 			},
 		},
 		{
 			name:       "not empty",
 			submatches: []string{"m0", "m1", "m2"},
-			expectMatch: []wantMatch{
-				{target: nil, fail: true},
-				{target: []string{"m1"}, fail: true},
-				{target: []string{"m2", "m1"}, fail: true},
-				{target: []string{"m1", "m2"}, fail: false},
+			wantMatch: []wantMatch{
+				{target: nil, result: failure},
+				{target: []string{"m1"}, result: failure},
+				{target: []string{"m2", "m1"}, result: failure},
+				{target: []string{"m1", "m2"}, result: success},
 			},
 		},
 	}
@@ -208,21 +201,12 @@ func TestMatch_Values(t *testing.T) {
 		t.Run(tc.name, func(t *testing.T) {
 			reporter := newMockReporter(t)
 
-			for _, match := range tc.expectMatch {
-				if match.fail {
-					NewMatch(reporter, tc.submatches, nil).NotValues(match.target...).
-						chain.assertNotFailed(t)
-
-					NewMatch(reporter, tc.submatches, nil).Values(match.target...).
-						chain.assertFailed(t)
-
-				} else {
-					NewMatch(reporter, tc.submatches, nil).Values(match.target...).
-						chain.assertNotFailed(t)
+			for _, match := range tc.wantMatch {
+				NewMatch(reporter, tc.submatches, nil).Values(match.target...).
+					chain.assert(t, match.result)
 
-					NewMatch(reporter, tc.submatches, nil).NotValues(match.target...).
-						chain.assertFailed(t)
-				}
+				NewMatch(reporter, tc.submatches, nil).NotValues(match.target...).
+					chain.assert(t, !match.result)
 			}
 		})
 	}
diff --git a/mocks_test.go b/mocks_test.go
index 2ed1957..80bcb96 100644
--- a/mocks_test.go
+++ b/mocks_test.go
@@ -151,6 +151,12 @@ func newMockChain(t *testing.T) *chain {
 	return newChainWithDefaults("test", newMockReporter(t))
 }
 
+func newFailedChain(t *testing.T) *chain {
+	chain := newMockChain(t)
+	chain.setFlags(flagFailed)
+	return chain
+}
+
 type mockLogger struct {
 	testing     *testing.T
 	logged      bool
diff --git a/number_test.go b/number_test.go
index ca845fd..957b346 100644
--- a/number_test.go
+++ b/number_test.go
@@ -196,23 +196,11 @@ func TestNumber_IsEqual(t *testing.T) {
 			t.Run(tc.name, func(t *testing.T) {
 				reporter := newMockReporter(t)
 
-				if tc.isEqual {
-					NewNumber(reporter, tc.number).
-						IsEqual(tc.value).
-						chain.assertNotFailed(t)
-
-					NewNumber(reporter, tc.number).
-						NotEqual(tc.value).
-						chain.assertFailed(t)
-				} else {
-					NewNumber(reporter, tc.number).
-						NotEqual(tc.value).
-						chain.assertNotFailed(t)
-
-					NewNumber(reporter, tc.number).
-						IsEqual(tc.value).
-						chain.assertFailed(t)
-				}
+				NewNumber(reporter, tc.number).IsEqual(tc.value).
+					chain.assertOK(t, tc.isEqual)
+
+				NewNumber(reporter, tc.number).NotEqual(tc.value).
+					chain.assertOK(t, !tc.isEqual)
 			})
 		}
 	})
@@ -315,25 +303,11 @@ func TestNumber_InDelta(t *testing.T) {
 		t.Run(tc.name, func(t *testing.T) {
 			reporter := newMockReporter(t)
 
-			if tc.isInDelta {
-				NewNumber(reporter, tc.number).
-					InDelta(tc.value, tc.delta).
-					chain.assertNotFailed(t)
-			} else {
-				NewNumber(reporter, tc.number).
-					InDelta(tc.value, tc.delta).
-					chain.assertFailed(t)
-			}
-
-			if tc.isNotInDelta {
-				NewNumber(reporter, tc.number).
-					NotInDelta(tc.value, tc.delta).
-					chain.assertNotFailed(t)
-			} else {
-				NewNumber(reporter, tc.number).
-					NotInDelta(tc.value, tc.delta).
-					chain.assertFailed(t)
-			}
+			NewNumber(reporter, tc.number).InDelta(tc.value, tc.delta).
+				chain.assertOK(t, tc.isInDelta)
+
+			NewNumber(reporter, tc.number).NotInDelta(tc.value, tc.delta).
+				chain.assertOK(t, tc.isNotInDelta)
 		})
 	}
 }
@@ -402,25 +376,11 @@ func TestNumber_InRange(t *testing.T) {
 			t.Run(tc.name, func(t *testing.T) {
 				reporter := newMockReporter(t)
 
-				if tc.isInRange {
-					NewNumber(reporter, tc.number).
-						InRange(tc.min, tc.max).
-						chain.assertNotFailed(t)
-				} else {
-					NewNumber(reporter, tc.number).
-						InRange(tc.min, tc.max).
-						chain.assertFailed(t)
-				}
-
-				if tc.isNotInRange {
-					NewNumber(reporter, tc.number).
-						NotInRange(tc.min, tc.max).
-						chain.assertNotFailed(t)
-				} else {
-					NewNumber(reporter, tc.number).
-						NotInRange(tc.min, tc.max).
-						chain.assertFailed(t)
-				}
+				NewNumber(reporter, tc.number).InRange(tc.min, tc.max).
+					chain.assertOK(t, tc.isInRange)
+
+				NewNumber(reporter, tc.number).NotInRange(tc.min, tc.max).
+					chain.assertOK(t, tc.isNotInRange)
 			})
 		}
 	})
@@ -449,23 +409,11 @@ func TestNumber_InRange(t *testing.T) {
 		reporter := newMockReporter(t)
 
 		for _, tc := range cases {
-			if tc.isInRange {
-				NewNumber(reporter, tc.number).
-					InRange(tc.min, tc.max).
-					chain.assertNotFailed(t)
-
-				NewNumber(reporter, tc.number).
-					NotInRange(tc.min, tc.max).
-					chain.assertFailed(t)
-			} else {
-				NewNumber(reporter, tc.number).
-					NotInRange(tc.min, tc.max).
-					chain.assertNotFailed(t)
-
-				NewNumber(reporter, tc.number).
-					InRange(tc.min, tc.max).
-					chain.assertFailed(t)
-			}
+			NewNumber(reporter, tc.number).InRange(tc.min, tc.max).
+				chain.assertOK(t, tc.isInRange)
+
+			NewNumber(reporter, tc.number).NotInRange(tc.min, tc.max).
+				chain.assertOK(t, !tc.isInRange)
 		}
 	})
 
@@ -536,25 +484,11 @@ func TestNumber_InList(t *testing.T) {
 			t.Run(tc.name, func(t *testing.T) {
 				reporter := newMockReporter(t)
 
-				if tc.isInList {
-					NewNumber(reporter, tc.number).
-						InList(tc.list...).
-						chain.assertNotFailed(t)
-				} else {
-					NewNumber(reporter, tc.number).
-						InList(tc.list...).
-						chain.assertFailed(t)
-				}
-
-				if tc.isNotInList {
-					NewNumber(reporter, tc.number).
-						NotInList(tc.list...).
-						chain.assertNotFailed(t)
-				} else {
-					NewNumber(reporter, tc.number).
-						NotInList(tc.list...).
-						chain.assertFailed(t)
-				}
+				NewNumber(reporter, tc.number).InList(tc.list...).
+					chain.assertOK(t, tc.isInList)
+
+				NewNumber(reporter, tc.number).NotInList(tc.list...).
+					chain.assertOK(t, tc.isNotInList)
 			})
 		}
 	})
@@ -585,23 +519,11 @@ func TestNumber_InList(t *testing.T) {
 		reporter := newMockReporter(t)
 
 		for _, tc := range cases {
-			if tc.isInList {
-				NewNumber(reporter, tc.number).
-					InList(tc.list...).
-					chain.assertNotFailed(t)
-
-				NewNumber(reporter, tc.number).
-					NotInList(tc.list...).
-					chain.assertFailed(t)
-			} else {
-				NewNumber(reporter, tc.number).
-					NotInList(tc.list...).
-					chain.assertNotFailed(t)
-
-				NewNumber(reporter, tc.number).
-					InList(tc.list...).
-					chain.assertFailed(t)
-			}
+			NewNumber(reporter, tc.number).InList(tc.list...).
+				chain.assertOK(t, tc.isInList)
+
+			NewNumber(reporter, tc.number).NotInList(tc.list...).
+				chain.assertOK(t, !tc.isInList)
 		}
 	})
 
@@ -658,25 +580,11 @@ func TestNumber_IsGreater(t *testing.T) {
 			t.Run(tc.name, func(t *testing.T) {
 				reporter := newMockReporter(t)
 
-				if tc.isGt {
-					NewNumber(reporter, tc.number).
-						Gt(tc.value).
-						chain.assertNotFailed(t)
-				} else {
-					NewNumber(reporter, tc.number).
-						Gt(tc.value).
-						chain.assertFailed(t)
-				}
-
-				if tc.isGe {
-					NewNumber(reporter, tc.number).
-						Ge(tc.value).
-						chain.assertNotFailed(t)
-				} else {
-					NewNumber(reporter, tc.number).
-						Ge(tc.value).
-						chain.assertFailed(t)
-				}
+				NewNumber(reporter, tc.number).Gt(tc.value).
+					chain.assertOK(t, tc.isGt)
+
+				NewNumber(reporter, tc.number).Ge(tc.value).
+					chain.assertOK(t, tc.isGe)
 			})
 		}
 	})
@@ -744,25 +652,11 @@ func TestNumber_IsLesser(t *testing.T) {
 			t.Run(tc.name, func(t *testing.T) {
 				reporter := newMockReporter(t)
 
-				if tc.isLt {
-					NewNumber(reporter, tc.number).
-						Lt(tc.value).
-						chain.assertNotFailed(t)
-				} else {
-					NewNumber(reporter, tc.number).
-						Lt(tc.value).
-						chain.assertFailed(t)
-				}
-
-				if tc.isLe {
-					NewNumber(reporter, tc.number).
-						Le(tc.value).
-						chain.assertNotFailed(t)
-				} else {
-					NewNumber(reporter, tc.number).
-						Le(tc.value).
-						chain.assertFailed(t)
-				}
+				NewNumber(reporter, tc.number).Lt(tc.value).
+					chain.assertOK(t, tc.isLt)
+
+				NewNumber(reporter, tc.number).Le(tc.value).
+					chain.assertOK(t, tc.isLe)
 			})
 		}
 	})
@@ -907,29 +801,20 @@ func TestNumber_IsInt(t *testing.T) {
 			t.Run(tc.name, func(t *testing.T) {
 				reporter := newMockReporter(t)
 
-				if tc.isInt {
-					NewNumber(reporter, tc.value).IsInt().chain.assertNotFailed(t)
-					NewNumber(reporter, tc.value).NotInt().chain.assertFailed(t)
-				} else {
-					NewNumber(reporter, tc.value).IsInt().chain.assertFailed(t)
-					NewNumber(reporter, tc.value).NotInt().chain.assertNotFailed(t)
-				}
-
-				if tc.isInt32 {
-					NewNumber(reporter, tc.value).IsInt(32).chain.assertNotFailed(t)
-					NewNumber(reporter, tc.value).NotInt(32).chain.assertFailed(t)
-				} else {
-					NewNumber(reporter, tc.value).IsInt(32).chain.assertFailed(t)
-					NewNumber(reporter, tc.value).NotInt(32).chain.assertNotFailed(t)
-				}
-
-				if tc.isInt16 {
-					NewNumber(reporter, tc.value).IsInt(16).chain.assertNotFailed(t)
-					NewNumber(reporter, tc.value).NotInt(16).chain.assertFailed(t)
-				} else {
-					NewNumber(reporter, tc.value).IsInt(16).chain.assertFailed(t)
-					NewNumber(reporter, tc.value).NotInt(16).chain.assertNotFailed(t)
-				}
+				NewNumber(reporter, tc.value).IsInt().
+					chain.assertOK(t, tc.isInt)
+				NewNumber(reporter, tc.value).NotInt().
+					chain.assertOK(t, !tc.isInt)
+
+				NewNumber(reporter, tc.value).IsInt(32).
+					chain.assertOK(t, tc.isInt32)
+				NewNumber(reporter, tc.value).NotInt(32).
+					chain.assertOK(t, !tc.isInt32)
+
+				NewNumber(reporter, tc.value).IsInt(16).
+					chain.assertOK(t, tc.isInt16)
+				NewNumber(reporter, tc.value).NotInt(16).
+					chain.assertOK(t, !tc.isInt16)
 			})
 		}
 	})
@@ -939,19 +824,16 @@ func TestNumber_IsInt(t *testing.T) {
 
 		NewNumber(reporter, 1234).IsInt(16, 32).
 			chain.assertFailed(t)
-
 		NewNumber(reporter, 1234).NotInt(16, 32).
 			chain.assertFailed(t)
 
 		NewNumber(reporter, 1234).IsInt(0).
 			chain.assertFailed(t)
-
 		NewNumber(reporter, 1234).NotInt(0).
 			chain.assertFailed(t)
 
 		NewNumber(reporter, 1234).IsInt(-16).
 			chain.assertFailed(t)
-
 		NewNumber(reporter, 1234).NotInt(-16).
 			chain.assertFailed(t)
 	})
@@ -1049,29 +931,20 @@ func TestNumber_IsUint(t *testing.T) {
 			t.Run(tc.name, func(t *testing.T) {
 				reporter := newMockReporter(t)
 
-				if tc.isUint {
-					NewNumber(reporter, tc.value).IsUint().chain.assertNotFailed(t)
-					NewNumber(reporter, tc.value).NotUint().chain.assertFailed(t)
-				} else {
-					NewNumber(reporter, tc.value).IsUint().chain.assertFailed(t)
-					NewNumber(reporter, tc.value).NotUint().chain.assertNotFailed(t)
-				}
-
-				if tc.isUint32 {
-					NewNumber(reporter, tc.value).IsUint(32).chain.assertNotFailed(t)
-					NewNumber(reporter, tc.value).NotUint(32).chain.assertFailed(t)
-				} else {
-					NewNumber(reporter, tc.value).IsUint(32).chain.assertFailed(t)
-					NewNumber(reporter, tc.value).NotUint(32).chain.assertNotFailed(t)
-				}
-
-				if tc.isUint16 {
-					NewNumber(reporter, tc.value).IsUint(16).chain.assertNotFailed(t)
-					NewNumber(reporter, tc.value).NotUint(16).chain.assertFailed(t)
-				} else {
-					NewNumber(reporter, tc.value).IsUint(16).chain.assertFailed(t)
-					NewNumber(reporter, tc.value).NotUint(16).chain.assertNotFailed(t)
-				}
+				NewNumber(reporter, tc.value).IsUint().
+					chain.assertOK(t, tc.isUint)
+				NewNumber(reporter, tc.value).NotUint().
+					chain.assertOK(t, !tc.isUint)
+
+				NewNumber(reporter, tc.value).IsUint(32).
+					chain.assertOK(t, tc.isUint32)
+				NewNumber(reporter, tc.value).NotUint(32).
+					chain.assertOK(t, !tc.isUint32)
+
+				NewNumber(reporter, tc.value).IsUint(16).
+					chain.assertOK(t, tc.isUint16)
+				NewNumber(reporter, tc.value).NotUint(16).
+					chain.assertOK(t, !tc.isUint16)
 			})
 		}
 	})
@@ -1081,19 +954,16 @@ func TestNumber_IsUint(t *testing.T) {
 
 		NewNumber(reporter, 1234).IsUint(16, 32).
 			chain.assertFailed(t)
-
 		NewNumber(reporter, 1234).NotUint(16, 32).
 			chain.assertFailed(t)
 
 		NewNumber(reporter, 1234).IsUint(0).
 			chain.assertFailed(t)
-
 		NewNumber(reporter, 1234).NotUint(0).
 			chain.assertFailed(t)
 
 		NewNumber(reporter, 1234).IsUint(-16).
 			chain.assertFailed(t)
-
 		NewNumber(reporter, 1234).NotUint(-16).
 			chain.assertFailed(t)
 	})
@@ -1146,13 +1016,11 @@ func TestNumber_IsFinite(t *testing.T) {
 		t.Run(tc.name, func(t *testing.T) {
 			reporter := newMockReporter(t)
 
-			if tc.isFinite {
-				NewNumber(reporter, tc.value).IsFinite().chain.assertNotFailed(t)
-				NewNumber(reporter, tc.value).NotFinite().chain.assertFailed(t)
-			} else {
-				NewNumber(reporter, tc.value).IsFinite().chain.assertFailed(t)
-				NewNumber(reporter, tc.value).NotFinite().chain.assertNotFailed(t)
-			}
+			NewNumber(reporter, tc.value).IsFinite().
+				chain.assertOK(t, tc.isFinite)
+
+			NewNumber(reporter, tc.value).NotFinite().
+				chain.assertOK(t, !tc.isFinite)
 		})
 	}
 }
diff --git a/object_test.go b/object_test.go
index ed3b826..0fed45f 100644
--- a/object_test.go
+++ b/object_test.go
@@ -9,18 +9,18 @@ import (
 
 func TestObject_FailedChain(t *testing.T) {
 	check := func(value *Object) {
-		value.chain.assertFailed(t)
+		value.chain.assert(t, failure)
 
-		value.Path("$").chain.assertFailed(t)
+		value.Path("$").chain.assert(t, failure)
 		value.Schema("")
 		value.Alias("foo")
 
 		var target interface{}
 		value.Decode(&target)
 
-		value.Keys().chain.assertFailed(t)
-		value.Values().chain.assertFailed(t)
-		value.Value("foo").chain.assertFailed(t)
+		value.Keys().chain.assert(t, failure)
+		value.Values().chain.assert(t, failure)
+		value.Value("foo").chain.assert(t, failure)
 
 		value.IsEmpty()
 		value.NotEmpty()
@@ -65,9 +65,7 @@ func TestObject_FailedChain(t *testing.T) {
 	}
 
 	t.Run("failed chain", func(t *testing.T) {
-		chain := newMockChain(t)
-		chain.setFailed()
-
+		chain := newFailedChain(t)
 		value := newObject(chain, map[string]interface{}{})
 
 		check(value)
@@ -75,16 +73,13 @@ func TestObject_FailedChain(t *testing.T) {
 
 	t.Run("nil value", func(t *testing.T) {
 		chain := newMockChain(t)
-
 		value := newObject(chain, nil)
 
 		check(value)
 	})
 
 	t.Run("failed chain, nil value", func(t *testing.T) {
-		chain := newMockChain(t)
-		chain.setFailed()
-
+		chain := newFailedChain(t)
 		value := newObject(chain, nil)
 
 		check(value)
@@ -102,7 +97,7 @@ func TestObject_Constructors(t *testing.T) {
 		value := NewObject(reporter, test)
 
 		value.IsEqual(test)
-		value.chain.assertNotFailed(t)
+		value.chain.assert(t, success)
 	})
 
 	t.Run("config", func(t *testing.T) {
@@ -113,7 +108,7 @@ func TestObject_Constructors(t *testing.T) {
 		}, test)
 
 		value.IsEqual(test)
-		value.chain.assertNotFailed(t)
+		value.chain.assert(t, success)
 	})
 
 	t.Run("chain", func(t *testing.T) {
@@ -130,8 +125,8 @@ func TestObject_Constructors(t *testing.T) {
 
 		value := NewObject(reporter, nil)
 
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 	})
 }
 
@@ -152,7 +147,7 @@ func TestObject_Decode(t *testing.T) {
 		var target interface{}
 		value.Decode(&target)
 
-		value.chain.assertNotFailed(t)
+		value.chain.assert(t, success)
 		assert.Equal(t, target, m)
 	})
 
@@ -172,7 +167,7 @@ func TestObject_Decode(t *testing.T) {
 		var target map[string]interface{}
 		value.Decode(&target)
 
-		value.chain.assertNotFailed(t)
+		value.chain.assert(t, success)
 		assert.Equal(t, target, m)
 	})
 
@@ -207,7 +202,7 @@ func TestObject_Decode(t *testing.T) {
 		var target S
 		value.Decode(&target)
 
-		value.chain.assertNotFailed(t)
+		value.chain.assert(t, success)
 		assert.Equal(t, target, actualStruct)
 	})
 
@@ -226,7 +221,7 @@ func TestObject_Decode(t *testing.T) {
 
 		value.Decode(nil)
 
-		value.chain.assertFailed(t)
+		value.chain.assert(t, failure)
 	})
 
 	t.Run("target is unmarshable", func(t *testing.T) {
@@ -244,7 +239,7 @@ func TestObject_Decode(t *testing.T) {
 
 		value.Decode(123)
 
-		value.chain.assertFailed(t)
+		value.chain.assert(t, failure)
 	})
 }
 
@@ -290,53 +285,53 @@ func TestObject_Getters(t *testing.T) {
 	}
 
 	assert.Equal(t, m, value.Raw())
-	value.chain.assertNotFailed(t)
-	value.chain.clearFailed()
+	value.chain.assert(t, success)
+	value.chain.clear()
 
 	assert.Equal(t, m, value.Path("$").Raw())
-	value.chain.assertNotFailed(t)
-	value.chain.clearFailed()
+	value.chain.assert(t, success)
+	value.chain.clear()
 
 	value.Schema(`{"type": "object"}`)
-	value.chain.assertNotFailed(t)
-	value.chain.clearFailed()
+	value.chain.assert(t, success)
+	value.chain.clear()
 
 	value.Schema(`{"type": "array"}`)
-	value.chain.assertFailed(t)
-	value.chain.clearFailed()
+	value.chain.assert(t, failure)
+	value.chain.clear()
 
 	value.Keys().ContainsOnly(keys...)
-	value.chain.assertNotFailed(t)
-	value.chain.clearFailed()
+	value.chain.assert(t, success)
+	value.chain.clear()
 
 	value.Values().ContainsOnly(values...)
-	value.chain.assertNotFailed(t)
-	value.chain.clearFailed()
+	value.chain.assert(t, success)
+	value.chain.clear()
 
 	assert.Equal(t, m["foo"], value.Value("foo").Raw())
-	value.chain.assertNotFailed(t)
-	value.chain.clearFailed()
+	value.chain.assert(t, success)
+	value.chain.clear()
 
 	assert.Equal(t, m["bar"], value.Value("bar").Raw())
-	value.chain.assertNotFailed(t)
-	value.chain.clearFailed()
+	value.chain.assert(t, success)
+	value.chain.clear()
 
 	assert.Equal(t, m["baz"], value.Value("baz").Raw())
-	value.chain.assertNotFailed(t)
-	value.chain.clearFailed()
+	value.chain.assert(t, success)
+	value.chain.clear()
 
 	assert.Equal(t, nil, value.Value("BAZ").Raw())
-	value.chain.assertFailed(t)
-	value.chain.clearFailed()
+	value.chain.assert(t, failure)
+	value.chain.clear()
 
 	it := value.Iter()
 	assert.Equal(t, 3, len(it))
 	assert.Equal(t, it["foo"].value, value.Value("foo").Raw())
 	assert.Equal(t, it["bar"].value, value.Value("bar").Raw())
-	it["foo"].chain.assertNotFailed(t)
-	it["bar"].chain.assertNotFailed(t)
-	value.chain.assertNotFailed(t)
-	value.chain.clearFailed()
+	it["foo"].chain.assert(t, success)
+	it["bar"].chain.assert(t, success)
+	value.chain.assert(t, success)
+	value.chain.clear()
 }
 
 func TestObject_IsEmpty(t *testing.T) {
@@ -346,12 +341,12 @@ func TestObject_IsEmpty(t *testing.T) {
 		value := NewObject(reporter, map[string]interface{}{})
 
 		value.IsEmpty()
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotEmpty()
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 	})
 
 	t.Run("one empty element", func(t *testing.T) {
@@ -360,12 +355,12 @@ func TestObject_IsEmpty(t *testing.T) {
 		value := NewObject(reporter, map[string]interface{}{"": nil})
 
 		value.IsEmpty()
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotEmpty()
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 	})
 }
 
@@ -378,20 +373,20 @@ func TestObject_IsEqual(t *testing.T) {
 		assert.Equal(t, map[string]interface{}{}, value.Raw())
 
 		value.IsEqual(map[string]interface{}{})
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotEqual(map[string]interface{}{})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.IsEqual(map[string]interface{}{"": nil})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotEqual(map[string]interface{}{"": nil})
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 	})
 
 	t.Run("not empty", func(t *testing.T) {
@@ -402,44 +397,44 @@ func TestObject_IsEqual(t *testing.T) {
 		assert.Equal(t, map[string]interface{}{"foo": 123.0}, value.Raw())
 
 		value.IsEqual(map[string]interface{}{})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotEqual(map[string]interface{}{})
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.IsEqual(map[string]interface{}{"FOO": 123.0})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotEqual(map[string]interface{}{"FOO": 123.0})
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.IsEqual(map[string]interface{}{"foo": 456.0})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotEqual(map[string]interface{}{"foo": 456.0})
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.IsEqual(map[string]interface{}{"foo": 123.0})
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotEqual(map[string]interface{}{"foo": 123.0})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.IsEqual(nil)
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotEqual(nil)
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 	})
 
 	t.Run("struct", func(t *testing.T) {
@@ -471,20 +466,20 @@ func TestObject_IsEqual(t *testing.T) {
 		}
 
 		value.IsEqual(s)
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotEqual(s)
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.IsEqual(S{})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotEqual(S{})
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 	})
 
 	t.Run("canonization", func(t *testing.T) {
@@ -498,20 +493,20 @@ func TestObject_IsEqual(t *testing.T) {
 		value := NewObject(reporter, map[string]interface{}{"foo": 123})
 
 		value.IsEqual(map[string]interface{}{"foo": 123.0})
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotEqual(map[string]interface{}{"foo": 123.0})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.IsEqual(myMap{"foo": myInt(123)})
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotEqual(myMap{"foo": myInt(123)})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 	})
 }
 
@@ -525,55 +520,55 @@ func TestObject_InList(t *testing.T) {
 			map[string]interface{}{"FOO": 123.0},
 			map[string]interface{}{"BAR": 456.0},
 		)
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotInList(
 			map[string]interface{}{"FOO": 123.0},
 			map[string]interface{}{"BAR": 456.0},
 		)
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.InList(
 			map[string]interface{}{"foo": 456.0},
 			map[string]interface{}{"bar": 123.0},
 		)
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotInList(
 			map[string]interface{}{"foo": 456.0},
 			map[string]interface{}{"bar": 123.0},
 		)
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.InList(
 			map[string]interface{}{"foo": 123.0},
 			map[string]interface{}{"bar": 456.0},
 		)
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotInList(
 			map[string]interface{}{"foo": 123.0},
 			map[string]interface{}{"bar": 456.0},
 		)
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.InList(struct {
 			Foo float64 `json:"foo"`
 		}{Foo: 123.00})
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotInList(struct {
 			Foo float64 `json:"foo"`
 		}{Foo: 123.00})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 	})
 
 	t.Run("empty", func(t *testing.T) {
@@ -582,12 +577,12 @@ func TestObject_InList(t *testing.T) {
 		value := NewObject(reporter, map[string]interface{}{})
 
 		value.InList(map[string]interface{}{})
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotInList(map[string]interface{}{})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 	})
 
 	t.Run("not object", func(t *testing.T) {
@@ -596,20 +591,20 @@ func TestObject_InList(t *testing.T) {
 		value := NewObject(reporter, map[string]interface{}{"foo": 123.0})
 
 		value.InList(map[string]interface{}{"bar": 123.0}, "NOT OBJECT")
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotInList(map[string]interface{}{"bar": 123.0}, "NOT OBJECT")
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.InList(map[string]interface{}{"foo": 123.0}, "NOT OBJECT")
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotInList(map[string]interface{}{"foo": 123.0}, "NOT OBJECT")
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 	})
 
 	t.Run("invalid argument", func(t *testing.T) {
@@ -618,28 +613,28 @@ func TestObject_InList(t *testing.T) {
 		value := NewObject(reporter, map[string]interface{}{})
 
 		value.InList()
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotInList()
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.InList(nil)
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotInList(nil)
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.InList(func() {})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotInList(func() {})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 	})
 
 	t.Run("canonization", func(t *testing.T) {
@@ -653,20 +648,20 @@ func TestObject_InList(t *testing.T) {
 		value := NewObject(reporter, map[string]interface{}{"foo": 123, "bar": 456})
 
 		value.InList(myMap{"foo": 123.0, "bar": 456.0})
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotInList(myMap{"foo": 123.0, "bar": 456.0})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.InList(myMap{"foo": "123", "bar": myInt(456.0)})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotInList(myMap{"foo": "123", "bar": myInt(456.0)})
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 	})
 }
 
@@ -676,28 +671,28 @@ func TestObject_ContainsKey(t *testing.T) {
 	value := NewObject(reporter, map[string]interface{}{"foo": 123, "bar": ""})
 
 	value.ContainsKey("foo")
-	value.chain.assertNotFailed(t)
-	value.chain.clearFailed()
+	value.chain.assert(t, success)
+	value.chain.clear()
 
 	value.NotContainsKey("foo")
-	value.chain.assertFailed(t)
-	value.chain.clearFailed()
+	value.chain.assert(t, failure)
+	value.chain.clear()
 
 	value.ContainsKey("bar")
-	value.chain.assertNotFailed(t)
-	value.chain.clearFailed()
+	value.chain.assert(t, success)
+	value.chain.clear()
 
 	value.NotContainsKey("bar")
-	value.chain.assertFailed(t)
-	value.chain.clearFailed()
+	value.chain.assert(t, failure)
+	value.chain.clear()
 
 	value.ContainsKey("BAR")
-	value.chain.assertFailed(t)
-	value.chain.clearFailed()
+	value.chain.assert(t, failure)
+	value.chain.clear()
 
 	value.NotContainsKey("BAR")
-	value.chain.assertNotFailed(t)
-	value.chain.clearFailed()
+	value.chain.assert(t, success)
+	value.chain.clear()
 }
 
 func TestObject_ContainsValue(t *testing.T) {
@@ -707,28 +702,28 @@ func TestObject_ContainsValue(t *testing.T) {
 		value := NewObject(reporter, map[string]interface{}{"foo": 123, "bar": "xxx"})
 
 		value.ContainsValue(123)
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotContainsValue(123)
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.ContainsValue("xxx")
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotContainsValue("xxx")
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.ContainsValue("XXX")
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotContainsValue("XXX")
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 	})
 
 	t.Run("struct", func(t *testing.T) {
@@ -765,20 +760,20 @@ func TestObject_ContainsValue(t *testing.T) {
 		}
 
 		value.ContainsValue(barValue)
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotContainsValue(barValue)
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.ContainsValue(bazValue)
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotContainsValue(bazValue)
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 	})
 
 	t.Run("canonization", func(t *testing.T) {
@@ -791,12 +786,12 @@ func TestObject_ContainsValue(t *testing.T) {
 		value := NewObject(reporter, map[string]interface{}{"foo": 123, "bar": 789})
 
 		value.ContainsValue(myInt(789.0))
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotContainsValue(myInt(789.0))
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 	})
 
 	t.Run("invalid argument", func(t *testing.T) {
@@ -805,12 +800,12 @@ func TestObject_ContainsValue(t *testing.T) {
 		value := NewObject(reporter, map[string]interface{}{"foo": 123, "bar": "xxx"})
 
 		value.ContainsValue(make(chan int))
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotContainsValue(make(chan int))
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 	})
 }
 
@@ -835,12 +830,12 @@ func TestObject_ContainsSubset(t *testing.T) {
 		}
 
 		value.ContainsSubset(submap1)
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotContainsSubset(submap1)
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		submap2 := map[string]interface{}{
 			"bar": []interface{}{"456", 789},
@@ -852,12 +847,12 @@ func TestObject_ContainsSubset(t *testing.T) {
 		}
 
 		value.ContainsSubset(submap2)
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotContainsSubset(submap2)
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 	})
 
 	t.Run("failure", func(t *testing.T) {
@@ -880,12 +875,12 @@ func TestObject_ContainsSubset(t *testing.T) {
 		}
 
 		value.ContainsSubset(submap1)
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotContainsSubset(submap1)
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		submap2 := map[string]interface{}{
 			"foo": 123,
@@ -893,12 +888,12 @@ func TestObject_ContainsSubset(t *testing.T) {
 		}
 
 		value.ContainsSubset(submap2)
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotContainsSubset(submap2)
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		submap3 := map[string]interface{}{
 			"baz": map[string]interface{}{
@@ -910,20 +905,20 @@ func TestObject_ContainsSubset(t *testing.T) {
 		}
 
 		value.ContainsSubset(submap3)
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotContainsSubset(submap3)
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.ContainsSubset(nil)
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotContainsSubset(nil)
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 	})
 
 	t.Run("struct", func(t *testing.T) {
@@ -965,20 +960,20 @@ func TestObject_ContainsSubset(t *testing.T) {
 		}
 
 		value.ContainsSubset(submap)
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotContainsSubset(submap)
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.ContainsSubset(S{})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotContainsSubset(S{})
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 	})
 
 	t.Run("canonization", func(t *testing.T) {
@@ -1004,12 +999,12 @@ func TestObject_ContainsSubset(t *testing.T) {
 		}
 
 		value.ContainsSubset(submap)
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotContainsSubset(submap)
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 	})
 }
 
@@ -1026,44 +1021,44 @@ func TestObject_HasValue(t *testing.T) {
 		})
 
 		value.HasValue("foo", 123)
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotHasValue("foo", 123)
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.HasValue("bar", []interface{}{"456", 789})
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotHasValue("bar", []interface{}{"456", 789})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.HasValue("baz", map[string]interface{}{"a": "b"})
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotHasValue("baz", map[string]interface{}{"a": "b"})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.HasValue("baz", func() {})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotHasValue("baz", func() {})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.HasValue("BAZ", 777)
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotHasValue("BAZ", 777)
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 	})
 
 	t.Run("struct", func(t *testing.T) {
@@ -1099,20 +1094,20 @@ func TestObject_HasValue(t *testing.T) {
 		}
 
 		value.HasValue("baz", baz)
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotHasValue("baz", baz)
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.HasValue("baz", Baz{})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.NotHasValue("baz", Baz{})
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 	})
 
 	t.Run("canonization", func(t *testing.T) {
@@ -1133,20 +1128,20 @@ func TestObject_HasValue(t *testing.T) {
 		})
 
 		value.HasValue("bar", myArray{"456", myInt(789)})
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotHasValue("bar", myArray{"456", myInt(789)})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 
 		value.HasValue("baz", myMap{"a": "b"})
-		value.chain.assertNotFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, success)
+		value.chain.clear()
 
 		value.NotHasValue("baz", myMap{"a": "b"})
-		value.chain.assertFailed(t)
-		value.chain.clearFailed()
+		value.chain.assert(t, failure)
+		value.chain.clear()
 	})
 }
 
@@ -1166,7 +1161,7 @@ func TestObject_Every(t *testing.T) {
 		})
 
 		assert.Equal(t, 3, invoked)
-		object.chain.assertNotFailed(t)
+		object.chain.assert(t, success)
 	})
 
 	t.Run("check key", func(t *testing.T) {
@@ -1193,7 +1188,7 @@ func TestObject_Every(t *testing.T) {
 		})
 
 		assert.Equal(t, 3, invoked)
-		object.chain.assertNotFailed(t)
+		object.chain.assert(t, success)
 	})
 
 	t.Run("empty object", func(t *testing.T) {
@@ -1207,7 +1202,7 @@ func TestObject_Every(t *testing.T) {
 		})
 
 		assert.Equal(t, 0, invoked)
-		object.chain.assertNotFailed(t)
+		object.chain.assert(t, success)
 	})
 
 	t.Run("one assertion fails", func(t *testing.T) {
@@ -1221,7 +1216,7 @@ func TestObject_Every(t *testing.T) {
 		})
 
 		assert.Equal(t, 2, invoked)
-		object.chain.assertFailed(t)
+		object.chain.assert(t, failure)
 	})
 
 	t.Run("all assertions fail", func(t *testing.T) {
@@ -1235,7 +1230,7 @@ func TestObject_Every(t *testing.T) {
 		})
 
 		assert.Equal(t, 2, invoked)
-		object.chain.assertFailed(t)
+		object.chain.assert(t, failure)
 	})
 
 	t.Run("call order", func(t *testing.T) {
@@ -1262,7 +1257,7 @@ func TestObject_Every(t *testing.T) {
 		reporter := newMockReporter(t)
 		object := NewObject(reporter, map[string]interface{}{})
 		object.Every((func(key string, value *Value))(nil))
-		object.chain.assertFailed(t)
+		object.chain.assert(t, failure)
 	})
 }
 
@@ -1289,7 +1284,7 @@ func TestObject_Transform(t *testing.T) {
 			return value
 		})
 
-		newObject.chain.assertNotFailed(t)
+		newObject.chain.assert(t, success)
 	})
 
 	t.Run("transform value", func(t *testing.T) {
@@ -1332,7 +1327,7 @@ func TestObject_Transform(t *testing.T) {
 			return nil
 		})
 
-		newObject.chain.assertNotFailed(t)
+		newObject.chain.assert(t, success)
 	})
 
 	t.Run("call order", func(t *testing.T) {
@@ -1364,7 +1359,7 @@ func TestObject_Transform(t *testing.T) {
 
 		newObject := object.Transform(nil)
 
-		newObject.chain.assertFailed(t)
+		newObject.chain.assert(t, failure)
 	})
 
 	t.Run("canonization", func(t *testing.T) {
@@ -1394,7 +1389,7 @@ func TestObject_Transform(t *testing.T) {
 				"baz": "b",
 			},
 			newObject.Raw())
-		newObject.chain.assertNotFailed(t)
+		newObject.chain.assert(t, success)
 	})
 }
 
@@ -1416,8 +1411,8 @@ func TestObject_Filter(t *testing.T) {
 			"baz": "qux", "quux": "corge",
 		})
 
-		filteredObject.chain.assertNotFailed(t)
-		object.chain.assertNotFailed(t)
+		filteredObject.chain.assert(t, success)
+		object.chain.assert(t, success)
 	})
 
 	t.Run("elements of different types", func(t *testing.T) {
@@ -1437,8 +1432,8 @@ func TestObject_Filter(t *testing.T) {
 			"baz": 3.0, "qux": false,
 		})
 
-		filteredObject.chain.assertNotFailed(t)
-		object.chain.assertNotFailed(t)
+		filteredObject.chain.assert(t, success)
+		object.chain.assert(t, success)
 	})
 
 	t.Run("empty object", func(t *testing.T) {
@@ -1450,8 +1445,8 @@ func TestObject_Filter(t *testing.T) {
 		})
 		assert.Equal(t, map[string]interface{}{}, filteredObject.Raw())
 
-		filteredObject.chain.assertNotFailed(t)
-		object.chain.assertNotFailed(t)
+		filteredObject.chain.assert(t, success)
+		object.chain.assert(t, success)
 	})
 
 	t.Run("no match", func(t *testing.T) {
@@ -1471,8 +1466,8 @@ func TestObject_Filter(t *testing.T) {
 			"baz": "qux", "quux": "corge",
 		})
 
-		filteredObject.chain.assertNotFailed(t)
-		object.chain.assertNotFailed(t)
+		filteredObject.chain.assert(t, success)
+		object.chain.assert(t, success)
 	})
 
 	t.Run("assertion fails", func(t *testing.T) {
@@ -1493,8 +1488,8 @@ func TestObject_Filter(t *testing.T) {
 			"qux": "quux",
 		})
 
-		filteredObject.chain.assertNotFailed(t)
-		object.chain.assertNotFailed(t)
+		filteredObject.chain.assert(t, success)
+		object.chain.assert(t, success)
 	})
 
 	t.Run("call order", func(t *testing.T) {
@@ -1521,8 +1516,8 @@ func TestObject_Filter(t *testing.T) {
 		reporter := newMockReporter(t)
 		object := NewObject(reporter, map[string]interface{}{})
 		filteredObject := object.Filter((func(key string, value *Value) bool)(nil))
-		object.chain.assertFailed(t)
-		filteredObject.chain.assertFailed(t)
+		object.chain.assert(t, failure)
+		filteredObject.chain.assert(t, failure)
 	})
 }
 
@@ -1546,8 +1541,8 @@ func TestObject_Find(t *testing.T) {
 			"quux": "corge",
 		})
 
-		foundValue.chain.assertNotFailed(t)
-		object.chain.assertNotFailed(t)
+		foundValue.chain.assert(t, success)
+		object.chain.assert(t, success)
 	})
 
 	t.Run("elements of different types", func(t *testing.T) {
@@ -1572,8 +1567,8 @@ func TestObject_Find(t *testing.T) {
 			"quux": 2.0,
 		})
 
-		foundValue.chain.assertNotFailed(t)
-		object.chain.assertNotFailed(t)
+		foundValue.chain.assert(t, success)
+		object.chain.assert(t, success)
 	})
 
 	t.Run("no match", func(t *testing.T) {
@@ -1598,8 +1593,8 @@ func TestObject_Find(t *testing.T) {
 			"quux": 2.0,
 		})
 
-		foundValue.chain.assertFailed(t)
-		object.chain.assertFailed(t)
+		foundValue.chain.assert(t, failure)
+		object.chain.assert(t, failure)
 	})
 
 	t.Run("empty object", func(t *testing.T) {
@@ -1614,8 +1609,8 @@ func TestObject_Find(t *testing.T) {
 		assert.Equal(t, nil, foundValue.Raw())
 		assert.Equal(t, object.Raw(), map[string]interface{}{})
 
-		foundValue.chain.assertFailed(t)
-		object.chain.assertFailed(t)
+		foundValue.chain.assert(t, failure)
+		object.chain.assert(t, failure)
 	})
 
 	t.Run("predicate returns true, assertion fails, no match", func(t *testing.T) {
@@ -1636,8 +1631,8 @@ func TestObject_Find(t *testing.T) {
 			"bar": 2.0,
 		})
 
-		foundValue.chain.assertFailed(t)
-		object.chain.assertFailed(t)
+		foundValue.chain.assert(t, failure)
+		object.chain.assert(t, failure)
 	})
 
 	t.Run("predicate returns true, assertion fails, have match", func(t *testing.T) {
@@ -1660,8 +1655,8 @@ func TestObject_Find(t *testing.T) {
 			"baz": "str",
 		})
 
-		foundValue.chain.assertNotFailed(t)
-		object.chain.assertNotFailed(t)
+		foundValue.chain.assert(t, success)
+		object.chain.assert(t, success)
 	})
 
 	t.Run("invalid argument", func(t *testing.T) {
@@ -1679,8 +1674,8 @@ func TestObject_Find(t *testing.T) {
 			"bar": 2.0,
 		})
 
-		foundValue.chain.assertFailed(t)
-		object.chain.assertFailed(t)
+		foundValue.chain.assert(t, failure)
+		object.chain.assert(t, failure)
 	})
 }
 
@@ -1710,9 +1705,9 @@ func TestObject_FindAll(t *testing.T) {
 		})
 
 		for _, value := range foundValues {
-			value.chain.assertNotFailed(t)
+			value.chain.assert(t, success)
 		}
-		object.chain.assertNotFailed(t)
+		object.chain.assert(t, success)
 	})
 
 	t.Run("elements of different types", func(t *testing.T) {
@@ -1743,9 +1738,9 @@ func TestObject_FindAll(t *testing.T) {
 		})
 
 		for _, value := range foundValues {
-			value.chain.assertNotFailed(t)
+			value.chain.assert(t, success)
 		}
-		object.chain.assertNotFailed(t)
+		object.chain.assert(t, success)
 	})
 
 	t.Run("no match", func(t *testing.T) {
@@ -1775,9 +1770,9 @@ func TestObject_FindAll(t *testing.T) {
 		})
 
 		for _, value := range foundValues {
-			value.chain.assertNotFailed(t)
+			value.chain.assert(t, success)
 		}
-		object.chain.assertNotFailed(t)
+		object.chain.assert(t, success)
 	})
 
 	t.Run("empty object", func(t *testing.T) {
@@ -1797,9 +1792,9 @@ func TestObject_FindAll(t *testing.T) {
 		assert.Equal(t, object.Raw(), map[string]interface{}{})
 
 		for _, value := range foundValues {
-			value.chain.assertNotFailed(t)
+			value.chain.assert(t, success)
 		}
-		object.chain.assertNotFailed(t)
+		object.chain.assert(t, success)
 	})
 
 	t.Run("predicate returns true, assertion fails, no match", func(t *testing.T) {
@@ -1826,9 +1821,9 @@ func TestObject_FindAll(t *testing.T) {
 		})
 
 		for _, value := range foundValues {
-			value.chain.assertNotFailed(t)
+			value.chain.assert(t, success)
 		}
-		object.chain.assertNotFailed(t)
+		object.chain.assert(t, success)
 	})
 
 	t.Run("predicate returns true, assertion fails, have matches", func(t *testing.T) {
@@ -1859,9 +1854,9 @@ func TestObject_FindAll(t *testing.T) {
 		})
 
 		for _, value := range foundValues {
-			value.chain.assertNotFailed(t)
+			value.chain.assert(t, success)
 		}
-		object.chain.assertNotFailed(t)
+		object.chain.assert(t, success)
 	})
 
 	t.Run("invalid argument", func(t *testing.T) {
@@ -1885,9 +1880,9 @@ func TestObject_FindAll(t *testing.T) {
 		})
 
 		for _, value := range foundValues {
-			value.chain.assertFailed(t)
+			value.chain.assert(t, failure)
 		}
-		object.chain.assertFailed(t)
+		object.chain.assert(t, failure)
 	})
 }
 
@@ -1906,7 +1901,7 @@ func TestObject_NotFind(t *testing.T) {
 		})
 
 		assert.Same(t, object, afterObject)
-		object.chain.assertNotFailed(t)
+		object.chain.assert(t, success)
 	})
 
 	t.Run("have match", func(t *testing.T) {
@@ -1923,7 +1918,7 @@ func TestObject_NotFind(t *testing.T) {
 		})
 
 		assert.Same(t, object, afterObject)
-		object.chain.assertFailed(t)
+		object.chain.assert(t, failure)
 	})
 
 	t.Run("empty object", func(t *testing.T) {
@@ -1935,7 +1930,7 @@ func TestObject_NotFind(t *testing.T) {
 		})
 
 		assert.Same(t, object, afterObject)
-		object.chain.assertNotFailed(t)
+		object.chain.assert(t, success)
 	})
 
 	t.Run("predicate returns true, assertion fails, no match", func(t *testing.T) {
@@ -1951,7 +1946,7 @@ func TestObject_NotFind(t *testing.T) {
 		})
 
 		assert.Same(t, object, afterObject)
-		object.chain.assertNotFailed(t)
+		object.chain.assert(t, success)
 	})
 
 	t.Run("predicate returns true, assertion fails, have match", func(t *testing.T) {
@@ -1968,7 +1963,7 @@ func TestObject_NotFind(t *testing.T) {
 		})
 
 		assert.Same(t, object, afterObject)
-		object.chain.assertFailed(t)
+		object.chain.assert(t, failure)
 	})
 
 	t.Run("invalid argument", func(t *testing.T) {
@@ -1981,6 +1976,6 @@ func TestObject_NotFind(t *testing.T) {
 		afterObject := object.NotFind(nil)
 
 		assert.Same(t, object, afterObject)
-		object.chain.assertFailed(t)
+		object.chain.assert(t, failure)
 	})
 }
diff --git a/string_test.go b/string_test.go
index 71daed8..5f14881 100644
--- a/string_test.go
+++ b/string_test.go
@@ -195,17 +195,11 @@ func TestString_IsEmpty(t *testing.T) {
 		t.Run(tc.name, func(t *testing.T) {
 			reporter := newMockReporter(t)
 
-			if tc.isEmpty {
-				NewString(reporter, tc.str).IsEmpty().
-					chain.assertNotFailed(t)
-				NewString(reporter, tc.str).NotEmpty().
-					chain.assertFailed(t)
-			} else {
-				NewString(reporter, tc.str).IsEmpty().
-					chain.assertFailed(t)
-				NewString(reporter, tc.str).NotEmpty().
-					chain.assertNotFailed(t)
-			}
+			NewString(reporter, tc.str).IsEmpty().
+				chain.assertOK(t, tc.isEmpty)
+
+			NewString(reporter, tc.str).NotEmpty().
+				chain.assertOK(t, !tc.isEmpty)
 		})
 	}
 }
@@ -244,29 +238,15 @@ func TestString_IsEqual(t *testing.T) {
 	for _, tc := range cases {
 		reporter := newMockReporter(t)
 
-		if tc.isEqual {
-			NewString(reporter, tc.str).IsEqual(tc.value).
-				chain.assertNotFailed(t)
-			NewString(reporter, tc.str).NotEqual(tc.value).
-				chain.assertFailed(t)
-		} else {
-			NewString(reporter, tc.str).IsEqual(tc.value).
-				chain.assertFailed(t)
-			NewString(reporter, tc.str).NotEqual(tc.value).
-				chain.assertNotFailed(t)
-		}
+		NewString(reporter, tc.str).IsEqual(tc.value).
+			chain.assertOK(t, tc.isEqual)
+		NewString(reporter, tc.str).NotEqual(tc.value).
+			chain.assertOK(t, !tc.isEqual)
 
-		if tc.isEqualFold {
-			NewString(reporter, tc.str).IsEqualFold(tc.value).
-				chain.assertNotFailed(t)
-			NewString(reporter, tc.str).NotEqualFold(tc.value).
-				chain.assertFailed(t)
-		} else {
-			NewString(reporter, tc.str).IsEqualFold(tc.value).
-				chain.assertFailed(t)
-			NewString(reporter, tc.str).NotEqualFold(tc.value).
-				chain.assertNotFailed(t)
-		}
+		NewString(reporter, tc.str).IsEqualFold(tc.value).
+			chain.assertOK(t, tc.isEqualFold)
+		NewString(reporter, tc.str).NotEqualFold(tc.value).
+			chain.assertOK(t, !tc.isEqualFold)
 	}
 }
 
@@ -305,49 +285,29 @@ func TestString_InList(t *testing.T) {
 		for _, tc := range cases {
 			reporter := newMockReporter(t)
 
-			if tc.inList {
-				NewString(reporter, tc.str).InList(tc.value...).
-					chain.assertNotFailed(t)
-				NewString(reporter, tc.str).NotInList(tc.value...).
-					chain.assertFailed(t)
-			} else {
-				NewString(reporter, tc.str).InList(tc.value...).
-					chain.assertFailed(t)
-				NewString(reporter, tc.str).NotInList(tc.value...).
-					chain.assertNotFailed(t)
-			}
+			NewString(reporter, tc.str).InList(tc.value...).
+				chain.assertOK(t, tc.inList)
+			NewString(reporter, tc.str).NotInList(tc.value...).
+				chain.assertOK(t, !tc.inList)
 
-			if tc.inListFold {
-				NewString(reporter, tc.str).InListFold(tc.value...).
-					chain.assertNotFailed(t)
-				NewString(reporter, tc.str).NotInListFold(tc.value...).
-					chain.assertFailed(t)
-			} else {
-				NewString(reporter, tc.str).InListFold(tc.value...).
-					chain.assertFailed(t)
-				NewString(reporter, tc.str).NotInListFold(tc.value...).
-					chain.assertNotFailed(t)
-			}
+			NewString(reporter, tc.str).InListFold(tc.value...).
+				chain.assertOK(t, tc.inListFold)
+			NewString(reporter, tc.str).NotInListFold(tc.value...).
+				chain.assertOK(t, !tc.inListFold)
 		}
 	})
 
 	t.Run("invalid argument", func(t *testing.T) {
 		reporter := newMockReporter(t)
 
-		NewString(reporter, "foo").
-			InList(). // empty list
+		NewString(reporter, "foo").InList().
 			chain.assertFailed(t)
-
-		NewString(reporter, "foo").
-			NotInList(). // empty list
+		NewString(reporter, "foo").NotInList().
 			chain.assertFailed(t)
 
-		NewString(reporter, "foo").
-			InListFold(). // empty list
+		NewString(reporter, "foo").InListFold().
 			chain.assertFailed(t)
-
-		NewString(reporter, "foo").
-			NotInListFold(). // empty list
+		NewString(reporter, "foo").NotInListFold().
 			chain.assertFailed(t)
 	})
 }
@@ -386,29 +346,15 @@ func TestString_Contains(t *testing.T) {
 	for _, tc := range cases {
 		reporter := newMockReporter(t)
 
-		if tc.contains {
-			NewString(reporter, tc.str).Contains(tc.value).
-				chain.assertNotFailed(t)
-			NewString(reporter, tc.str).NotContains(tc.value).
-				chain.assertFailed(t)
-		} else {
-			NewString(reporter, tc.str).Contains(tc.value).
-				chain.assertFailed(t)
-			NewString(reporter, tc.str).NotContains(tc.value).
-				chain.assertNotFailed(t)
-		}
+		NewString(reporter, tc.str).Contains(tc.value).
+			chain.assertOK(t, tc.contains)
+		NewString(reporter, tc.str).NotContains(tc.value).
+			chain.assertOK(t, !tc.contains)
 
-		if tc.containsFold {
-			NewString(reporter, tc.str).ContainsFold(tc.value).
-				chain.assertNotFailed(t)
-			NewString(reporter, tc.str).NotContainsFold(tc.value).
-				chain.assertFailed(t)
-		} else {
-			NewString(reporter, tc.str).ContainsFold(tc.value).
-				chain.assertFailed(t)
-			NewString(reporter, tc.str).NotContainsFold(tc.value).
-				chain.assertNotFailed(t)
-		}
+		NewString(reporter, tc.str).ContainsFold(tc.value).
+			chain.assertOK(t, tc.containsFold)
+		NewString(reporter, tc.str).NotContainsFold(tc.value).
+			chain.assertOK(t, !tc.containsFold)
 	}
 }
 
@@ -474,29 +420,15 @@ func TestString_HasPrefix(t *testing.T) {
 	for _, tc := range cases {
 		reporter := newMockReporter(t)
 
-		if tc.hasPrefix {
-			NewString(reporter, tc.str).HasPrefix(tc.value).
-				chain.assertNotFailed(t)
-			NewString(reporter, tc.str).NotHasPrefix(tc.value).
-				chain.assertFailed(t)
-		} else {
-			NewString(reporter, tc.str).HasPrefix(tc.value).
-				chain.assertFailed(t)
-			NewString(reporter, tc.str).NotHasPrefix(tc.value).
-				chain.assertNotFailed(t)
-		}
+		NewString(reporter, tc.str).HasPrefix(tc.value).
+			chain.assertOK(t, tc.hasPrefix)
+		NewString(reporter, tc.str).NotHasPrefix(tc.value).
+			chain.assertOK(t, !tc.hasPrefix)
 
-		if tc.hasPrefixFold {
-			NewString(reporter, tc.str).HasPrefixFold(tc.value).
-				chain.assertNotFailed(t)
-			NewString(reporter, tc.str).NotHasPrefixFold(tc.value).
-				chain.assertFailed(t)
-		} else {
-			NewString(reporter, tc.str).HasPrefixFold(tc.value).
-				chain.assertFailed(t)
-			NewString(reporter, tc.str).NotHasPrefixFold(tc.value).
-				chain.assertNotFailed(t)
-		}
+		NewString(reporter, tc.str).HasPrefixFold(tc.value).
+			chain.assertOK(t, tc.hasPrefixFold)
+		NewString(reporter, tc.str).NotHasPrefixFold(tc.value).
+			chain.assertOK(t, !tc.hasPrefixFold)
 	}
 }
 
@@ -562,29 +494,15 @@ func TestString_HasSuffix(t *testing.T) {
 	for _, tc := range cases {
 		reporter := newMockReporter(t)
 
-		if tc.hasSuffix {
-			NewString(reporter, tc.str).HasSuffix(tc.value).
-				chain.assertNotFailed(t)
-			NewString(reporter, tc.str).NotHasSuffix(tc.value).
-				chain.assertFailed(t)
-		} else {
-			NewString(reporter, tc.str).HasSuffix(tc.value).
-				chain.assertFailed(t)
-			NewString(reporter, tc.str).NotHasSuffix(tc.value).
-				chain.assertNotFailed(t)
-		}
+		NewString(reporter, tc.str).HasSuffix(tc.value).
+			chain.assertOK(t, tc.hasSuffix)
+		NewString(reporter, tc.str).NotHasSuffix(tc.value).
+			chain.assertOK(t, !tc.hasSuffix)
 
-		if tc.hasSuffixFold {
-			NewString(reporter, tc.str).HasSuffixFold(tc.value).
-				chain.assertNotFailed(t)
-			NewString(reporter, tc.str).NotHasSuffixFold(tc.value).
-				chain.assertFailed(t)
-		} else {
-			NewString(reporter, tc.str).HasSuffixFold(tc.value).
-				chain.assertFailed(t)
-			NewString(reporter, tc.str).NotHasSuffixFold(tc.value).
-				chain.assertNotFailed(t)
-		}
+		NewString(reporter, tc.str).HasSuffixFold(tc.value).
+			chain.assertOK(t, tc.hasSuffixFold)
+		NewString(reporter, tc.str).NotHasSuffixFold(tc.value).
+			chain.assertOK(t, !tc.hasSuffixFold)
 	}
 }
 
@@ -670,33 +588,17 @@ func TestString_Match(t *testing.T) {
 
 				var value *String
 
-				if tc.match {
-					value = NewString(reporter, tc.str)
-					value.Match(tc.re).chain.assertNotFailed(t)
-					value.chain.assertNotFailed(t)
-
-					value = NewString(reporter, tc.str)
-					value.MatchAll(tc.re)
-					value.chain.assertNotFailed(t)
-				} else {
-					value = NewString(reporter, tc.str)
-					value.Match(tc.re).chain.assertFailed(t)
-					value.chain.assertFailed(t)
-
-					value = NewString(reporter, tc.str)
-					value.MatchAll(tc.re)
-					value.chain.assertFailed(t)
-				}
-
-				if tc.notMatch {
-					value = NewString(reporter, tc.str)
-					value.NotMatch(tc.re)
-					value.chain.assertNotFailed(t)
-				} else {
-					value = NewString(reporter, tc.str)
-					value.NotMatch(tc.re)
-					value.chain.assertFailed(t)
-				}
+				value = NewString(reporter, tc.str)
+				value.Match(tc.re).chain.assertOK(t, tc.match)
+				value.chain.assertOK(t, tc.match)
+
+				value = NewString(reporter, tc.str)
+				value.MatchAll(tc.re)
+				value.chain.assertOK(t, tc.match)
+
+				value = NewString(reporter, tc.str)
+				value.NotMatch(tc.re)
+				value.chain.assertOK(t, tc.notMatch)
 			})
 		}
 	})
@@ -718,17 +620,11 @@ func TestString_IsAscii(t *testing.T) {
 		t.Run(tc.str, func(t *testing.T) {
 			reporter := newMockReporter(t)
 
-			if tc.isASCII {
-				NewString(reporter, tc.str).IsASCII().
-					chain.assertNotFailed(t)
-				NewString(reporter, tc.str).NotASCII().
-					chain.assertFailed(t)
-			} else {
-				NewString(reporter, tc.str).IsASCII().
-					chain.assertFailed(t)
-				NewString(reporter, tc.str).NotASCII().
-					chain.assertNotFailed(t)
-			}
+			NewString(reporter, tc.str).IsASCII().
+				chain.assertOK(t, tc.isASCII)
+
+			NewString(reporter, tc.str).NotASCII().
+				chain.assertOK(t, !tc.isASCII)
 		})
 	}
 }
@@ -832,13 +728,11 @@ func TestString_AsNumber(t *testing.T) {
 			str := NewString(reporter, tc.str)
 			num := str.AsNumber(tc.base...)
 
+			str.chain.assertOK(t, !tc.fail)
+			num.chain.assertOK(t, !tc.fail)
 			if tc.fail {
-				str.chain.assertFailed(t)
-				num.chain.assertFailed(t)
 				assert.Equal(t, float64(0), num.Raw())
 			} else {
-				str.chain.assertNotFailed(t)
-				num.chain.assertNotFailed(t)
 				assert.Equal(t, tc.expectedNum, num.Raw())
 			}
 		})
diff --git a/value_test.go b/value_test.go
index 4d9ac6e..29fd382 100644
--- a/value_test.go
+++ b/value_test.go
@@ -11,24 +11,23 @@ import (
 )
 
 func TestValue_FailedChain(t *testing.T) {
-	chain := newMockChain(t)
-	chain.setFailed()
+	chain := newFailedChain(t)
 
 	value := newValue(chain, nil)
-	value.chain.assertFailed(t)
+	value.chain.assert(t, failure)
 
-	value.Path("$").chain.assertFailed(t)
+	value.Path("$").chain.assert(t, failure)
 	value.Schema("")
 	value.Alias("foo")
 
 	var target interface{}
 	value.Decode(target)
 
-	value.Object().chain.assertFailed(t)
-	value.Array().chain.assertFailed(t)
-	value.String().chain.assertFailed(t)
-	value.Number().chain.assertFailed(t)
-	value.Boolean().chain.assertFailed(t)
+	value.Object().chain.assert(t, failure)
+	value.Array().chain.assert(t, failure)
+	value.String().chain.assert(t, failure)
+	value.Number().chain.assert(t, failure)
+	value.Boolean().chain.assert(t, failure)
 
 	value.IsNull()
 	value.NotNull()
@@ -53,8 +52,8 @@ func TestValue_Constructors(t *testing.T) {
 		reporter := newMockReporter(t)
 		value := NewValue(reporter, "Test")
 		value.IsEqual("Test")
-		value.chain.assertNotFailed(t)
-		value.String().chain.assertNotFailed(t)
+		value.chain.assert(t, success)
+		value.String().chain.assert(t, success)
 	})
 
 	t.Run("config", func(t *testing.T) {
@@ -63,8 +62,8 @@ func TestValue_Constructors(t *testing.T) {
 			Reporter: reporter,
 		}, "Test")
 		value.IsEqual("Test")
-		value.chain.assertNotFailed(t)
-		value.String().chain.assertNotFailed(t)
+		value.chain.assert(t, success)
+		value.String().chain.assert(t, success)
 	})
 
 	t.Run("chain", func(t *testing.T) {
@@ -84,7 +83,7 @@ func TestValue_Decode(t *testing.T) {
 		var target interface{}
 		value.Decode(&target)
 
-		value.chain.assertNotFailed(t)
+		value.chain.assert(t, success)
 		assert.Equal(t, 123.0, target)
 	})
 
@@ -114,7 +113,7 @@ func TestValue_Decode(t *testing.T) {
 		var target S
 		value.Decode(&target)
 
-		value.chain.assertNotFailed(t)
+		value.chain.assert(t, success)
 		assert.Equal(t, target, actualStruct)
 	})
 
@@ -161,97 +160,97 @@ func TestValue_Getters(t *testing.T) {
 	t.Run("null", func(t *testing.T) {
 		var data interface{}
 
-		NewValue(reporter, data).Object().chain.assertFailed(t)
-		NewValue(reporter, data).Array().chain.assertFailed(t)
-		NewValue(reporter, data).String().chain.assertFailed(t)
-		NewValue(reporter, data).Number().chain.assertFailed(t)
-		NewValue(reporter, data).Boolean().chain.assertFailed(t)
-		NewValue(reporter, data).NotNull().chain.assertFailed(t)
-		NewValue(reporter, data).IsNull().chain.assertNotFailed(t)
+		NewValue(reporter, data).Object().chain.assert(t, failure)
+		NewValue(reporter, data).Array().chain.assert(t, failure)
+		NewValue(reporter, data).String().chain.assert(t, failure)
+		NewValue(reporter, data).Number().chain.assert(t, failure)
+		NewValue(reporter, data).Boolean().chain.assert(t, failure)
+		NewValue(reporter, data).NotNull().chain.assert(t, failure)
+		NewValue(reporter, data).IsNull().chain.assert(t, success)
 	})
 
 	t.Run("indirect null", func(t *testing.T) {
 		var data []interface{}
 
-		NewValue(reporter, data).Object().chain.assertFailed(t)
-		NewValue(reporter, data).Array().chain.assertFailed(t)
-		NewValue(reporter, data).String().chain.assertFailed(t)
-		NewValue(reporter, data).Number().chain.assertFailed(t)
-		NewValue(reporter, data).Boolean().chain.assertFailed(t)
-		NewValue(reporter, data).NotNull().chain.assertFailed(t)
-		NewValue(reporter, data).IsNull().chain.assertNotFailed(t)
+		NewValue(reporter, data).Object().chain.assert(t, failure)
+		NewValue(reporter, data).Array().chain.assert(t, failure)
+		NewValue(reporter, data).String().chain.assert(t, failure)
+		NewValue(reporter, data).Number().chain.assert(t, failure)
+		NewValue(reporter, data).Boolean().chain.assert(t, failure)
+		NewValue(reporter, data).NotNull().chain.assert(t, failure)
+		NewValue(reporter, data).IsNull().chain.assert(t, success)
 	})
 
 	t.Run("bad", func(t *testing.T) {
 		data := func() {}
 
-		NewValue(reporter, data).Object().chain.assertFailed(t)
-		NewValue(reporter, data).Array().chain.assertFailed(t)
-		NewValue(reporter, data).String().chain.assertFailed(t)
-		NewValue(reporter, data).Number().chain.assertFailed(t)
-		NewValue(reporter, data).Boolean().chain.assertFailed(t)
-		NewValue(reporter, data).NotNull().chain.assertFailed(t)
-		NewValue(reporter, data).IsNull().chain.assertFailed(t)
+		NewValue(reporter, data).Object().chain.assert(t, failure)
+		NewValue(reporter, data).Array().chain.assert(t, failure)
+		NewValue(reporter, data).String().chain.assert(t, failure)
+		NewValue(reporter, data).Number().chain.assert(t, failure)
+		NewValue(reporter, data).Boolean().chain.assert(t, failure)
+		NewValue(reporter, data).NotNull().chain.assert(t, failure)
+		NewValue(reporter, data).IsNull().chain.assert(t, failure)
 	})
 
 	t.Run("object", func(t *testing.T) {
 		data := map[string]interface{}{}
 
-		NewValue(reporter, data).Object().chain.assertNotFailed(t)
-		NewValue(reporter, data).Array().chain.assertFailed(t)
-		NewValue(reporter, data).String().chain.assertFailed(t)
-		NewValue(reporter, data).Number().chain.assertFailed(t)
-		NewValue(reporter, data).Boolean().chain.assertFailed(t)
-		NewValue(reporter, data).NotNull().chain.assertNotFailed(t)
-		NewValue(reporter, data).IsNull().chain.assertFailed(t)
+		NewValue(reporter, data).Object().chain.assert(t, success)
+		NewValue(reporter, data).Array().chain.assert(t, failure)
+		NewValue(reporter, data).String().chain.assert(t, failure)
+		NewValue(reporter, data).Number().chain.assert(t, failure)
+		NewValue(reporter, data).Boolean().chain.assert(t, failure)
+		NewValue(reporter, data).NotNull().chain.assert(t, success)
+		NewValue(reporter, data).IsNull().chain.assert(t, failure)
 	})
 
 	t.Run("array", func(t *testing.T) {
 		data := []interface{}{}
 
-		NewValue(reporter, data).Object().chain.assertFailed(t)
-		NewValue(reporter, data).Array().chain.assertNotFailed(t)
-		NewValue(reporter, data).String().chain.assertFailed(t)
-		NewValue(reporter, data).Number().chain.assertFailed(t)
-		NewValue(reporter, data).Boolean().chain.assertFailed(t)
-		NewValue(reporter, data).NotNull().chain.assertNotFailed(t)
-		NewValue(reporter, data).IsNull().chain.assertFailed(t)
+		NewValue(reporter, data).Object().chain.assert(t, failure)
+		NewValue(reporter, data).Array().chain.assert(t, success)
+		NewValue(reporter, data).String().chain.assert(t, failure)
+		NewValue(reporter, data).Number().chain.assert(t, failure)
+		NewValue(reporter, data).Boolean().chain.assert(t, failure)
+		NewValue(reporter, data).NotNull().chain.assert(t, success)
+		NewValue(reporter, data).IsNull().chain.assert(t, failure)
 	})
 
 	t.Run("string", func(t *testing.T) {
 		data := ""
 
-		NewValue(reporter, data).Object().chain.assertFailed(t)
-		NewValue(reporter, data).Array().chain.assertFailed(t)
-		NewValue(reporter, data).String().chain.assertNotFailed(t)
-		NewValue(reporter, data).Number().chain.assertFailed(t)
-		NewValue(reporter, data).Boolean().chain.assertFailed(t)
-		NewValue(reporter, data).NotNull().chain.assertNotFailed(t)
-		NewValue(reporter, data).IsNull().chain.assertFailed(t)
+		NewValue(reporter, data).Object().chain.assert(t, failure)
+		NewValue(reporter, data).Array().chain.assert(t, failure)
+		NewValue(reporter, data).String().chain.assert(t, success)
+		NewValue(reporter, data).Number().chain.assert(t, failure)
+		NewValue(reporter, data).Boolean().chain.assert(t, failure)
+		NewValue(reporter, data).NotNull().chain.assert(t, success)
+		NewValue(reporter, data).IsNull().chain.assert(t, failure)
 	})
 
 	t.Run("number", func(t *testing.T) {
 		data := 0.0
 
-		NewValue(reporter, data).Object().chain.assertFailed(t)
-		NewValue(reporter, data).Array().chain.assertFailed(t)
-		NewValue(reporter, data).String().chain.assertFailed(t)
-		NewValue(reporter, data).Number().chain.assertNotFailed(t)
-		NewValue(reporter, data).Boolean().chain.assertFailed(t)
-		NewValue(reporter, data).NotNull().chain.assertNotFailed(t)
-		NewValue(reporter, data).IsNull().chain.assertFailed(t)
+		NewValue(reporter, data).Object().chain.assert(t, failure)
+		NewValue(reporter, data).Array().chain.assert(t, failure)
+		NewValue(reporter, data).String().chain.assert(t, failure)
+		NewValue(reporter, data).Number().chain.assert(t, success)
+		NewValue(reporter, data).Boolean().chain.assert(t, failure)
+		NewValue(reporter, data).NotNull().chain.assert(t, success)
+		NewValue(reporter, data).IsNull().chain.assert(t, failure)
 	})
 
 	t.Run("boolean", func(t *testing.T) {
 		data := false
 
-		NewValue(reporter, data).Object().chain.assertFailed(t)
-		NewValue(reporter, data).Array().chain.assertFailed(t)
-		NewValue(reporter, data).String().chain.assertFailed(t)
-		NewValue(reporter, data).Number().chain.assertFailed(t)
-		NewValue(reporter, data).Boolean().chain.assertNotFailed(t)
-		NewValue(reporter, data).NotNull().chain.assertNotFailed(t)
-		NewValue(reporter, data).IsNull().chain.assertFailed(t)
+		NewValue(reporter, data).Object().chain.assert(t, failure)
+		NewValue(reporter, data).Array().chain.assert(t, failure)
+		NewValue(reporter, data).String().chain.assert(t, failure)
+		NewValue(reporter, data).Number().chain.assert(t, failure)
+		NewValue(reporter, data).Boolean().chain.assert(t, success)
+		NewValue(reporter, data).NotNull().chain.assert(t, success)
+		NewValue(reporter, data).IsNull().chain.assert(t, failure)
 	})
 }
 
@@ -261,19 +260,19 @@ func TestValue_GetObject(t *testing.T) {
 	cases := []struct {
 		name           string
 		data           interface{}
-		fail           bool
+		result         chainResult
 		expectedObject map[string]interface{}
 	}{
 		{
 			name:           "map",
 			data:           map[string]interface{}{"foo": 123.0},
-			fail:           false,
+			result:         success,
 			expectedObject: map[string]interface{}{"foo": 123.0},
 		},
 		{
 			name:           "myMap",
 			data:           myMap{"foo": 123.0},
-			fail:           false,
+			result:         success,
 			expectedObject: map[string]interface{}(myMap{"foo": 123.0}),
 		},
 	}
@@ -285,10 +284,9 @@ func TestValue_GetObject(t *testing.T) {
 			value := NewValue(reporter, tc.data)
 			inner := value.Object()
 
-			if tc.fail {
-				inner.chain.assertNotFailed(t)
-			} else {
-				inner.chain.assertNotFailed(t)
+			inner.chain.assert(t, tc.result)
+
+			if tc.result {
 				assert.Equal(t, tc.expectedObject, inner.Raw())
 			}
 		})
@@ -301,19 +299,19 @@ func TestValue_GetArray(t *testing.T) {
 	cases := []struct {
 		name          string
 		data          interface{}
-		fail          bool
+		result        chainResult
 		expectedArray []interface{}
 	}{
 		{
 			name:          "array",
 			data:          []interface{}{"foo", 123.0},
-			fail:          false,
+			result:        success,
 			expectedArray: []interface{}{"foo", 123.0},
 		},
 		{
 			name:          "myArray",
 			data:          myArray{"foo", 123.0},
-			fail:          false,
+			result:        success,
 			expectedArray: []interface{}(myArray{"foo", 123.0}),
 		},
 	}
@@ -325,12 +323,10 @@ func TestValue_GetArray(t *testing.T) {
 			value := NewValue(reporter, tc.data)
 			inner := value.Array()
 
-			if tc.fail {
-				value.chain.assertFailed(t)
-				inner.chain.assertFailed(t)
-			} else {
-				value.chain.assertNotFailed(t)
-				inner.chain.assertNotFailed(t)
+			value.chain.assert(t, tc.result)
+			inner.chain.assert(t, tc.result)
+
+			if tc.result {
 				assert.Equal(t, tc.expectedArray, inner.Raw())
 			}
 		})
@@ -343,19 +339,19 @@ func TestValue_GetString(t *testing.T) {
 	cases := []struct {
 		name           string
 		data           interface{}
-		fail           bool
+		result         chainResult
 		expectedString string
 	}{
 		{
 			name:           "string",
 			data:           "foo",
-			fail:           false,
+			result:         success,
 			expectedString: "foo",
 		},
 		{
 			name:           "myString",
 			data:           myString("foo"),
-			fail:           false,
+			result:         success,
 			expectedString: "foo",
 		},
 	}
@@ -367,12 +363,10 @@ func TestValue_GetString(t *testing.T) {
 			value := NewValue(reporter, tc.data)
 			inner := value.String()
 
-			if tc.fail {
-				value.chain.assertFailed(t)
-				inner.chain.assertFailed(t)
-			} else {
-				value.chain.assertNotFailed(t)
-				inner.chain.assertNotFailed(t)
+			value.chain.assert(t, tc.result)
+			inner.chain.assert(t, tc.result)
+
+			if tc.result {
 				assert.Equal(t, tc.expectedString, inner.Raw())
 			}
 		})
@@ -385,12 +379,12 @@ func TestValue_GetNumber(t *testing.T) {
 	cases := []struct {
 		name        string
 		data        interface{}
-		fail        bool
+		result      chainResult
 		expectedNum float64
 	}{
-		{name: "float", data: 123.0, fail: false, expectedNum: float64(123.0)},
-		{name: "integer", data: 123, fail: false, expectedNum: float64(123)},
-		{name: "myInt", data: myInt(123), fail: false, expectedNum: float64(myInt(123))},
+		{name: "float", data: 123.0, result: success, expectedNum: float64(123.0)},
+		{name: "integer", data: 123, result: success, expectedNum: float64(123)},
+		{name: "myInt", data: myInt(123), result: success, expectedNum: float64(myInt(123))},
 	}
 
 	for _, tc := range cases {
@@ -400,12 +394,10 @@ func TestValue_GetNumber(t *testing.T) {
 			value := NewValue(reporter, tc.data)
 			inner := value.Number()
 
-			if tc.fail {
-				value.chain.assertFailed(t)
-				inner.chain.assertFailed(t)
-			} else {
-				value.chain.assertNotFailed(t)
-				inner.chain.assertNotFailed(t)
+			value.chain.assert(t, tc.result)
+			inner.chain.assert(t, tc.result)
+
+			if tc.result {
 				assert.Equal(t, tc.expectedNum, inner.Raw())
 			}
 		})
@@ -418,12 +410,12 @@ func TestValue_GetBoolean(t *testing.T) {
 	cases := []struct {
 		name         string
 		data         interface{}
-		fail         bool
+		result       chainResult
 		expectedBool bool
 	}{
-		{name: "false", data: false, fail: false, expectedBool: false},
-		{name: "true", data: true, fail: false, expectedBool: true},
-		{name: "myTrue", data: myBool(true), fail: false, expectedBool: true},
+		{name: "false", data: false, result: success, expectedBool: false},
+		{name: "true", data: true, result: success, expectedBool: true},
+		{name: "myTrue", data: myBool(true), result: success, expectedBool: true},
 	}
 
 	for _, tc := range cases {
@@ -433,12 +425,10 @@ func TestValue_GetBoolean(t *testing.T) {
 			value := NewValue(reporter, tc.data)
 			inner := value.Boolean()
 
-			if tc.fail {
-				value.chain.assertFailed(t)
-				inner.chain.assertFailed(t)
-			} else {
-				value.chain.assertNotFailed(t)
-				inner.chain.assertNotFailed(t)
+			value.chain.assert(t, tc.result)
+			inner.chain.assert(t, tc.result)
+
+			if tc.result {
 				assert.Equal(t, tc.expectedBool, inner.Raw())
 			}
 		})
@@ -449,29 +439,21 @@ func TestValue_IsObject(t *testing.T) {
 	cases := []struct {
 		name       string
 		data       interface{}
-		wantObject bool
+		wantObject chainResult
 	}{
-		{name: "object", data: map[string]interface{}{"foo": 123.0}, wantObject: true},
-		{name: "string", data: "foo", wantObject: false},
+		{name: "object", data: map[string]interface{}{"foo": 123.0}, wantObject: success},
+		{name: "string", data: "foo", wantObject: failure},
 	}
 
 	for _, tc := range cases {
 		t.Run(tc.name, func(t *testing.T) {
 			reporter := newMockReporter(t)
 
-			if tc.wantObject {
-				NewValue(reporter, tc.data).IsObject().
-					chain.assertNotFailed(t)
-
-				NewValue(reporter, tc.data).NotObject().
-					chain.assertFailed(t)
-			} else {
-				NewValue(reporter, tc.data).NotObject().
-					chain.assertNotFailed(t)
+			NewValue(reporter, tc.data).IsObject().
+				chain.assert(t, tc.wantObject)
 
-				NewValue(reporter, tc.data).IsObject().
-					chain.assertFailed(t)
-			}
+			NewValue(reporter, tc.data).NotObject().
+				chain.assert(t, !tc.wantObject)
 		})
 	}
 }
@@ -480,29 +462,21 @@ func TestValue_IsArray(t *testing.T) {
 	cases := []struct {
 		name      string
 		data      interface{}
-		wantArray bool
+		wantArray chainResult
 	}{
-		{name: "array", data: []interface{}{"foo", "123"}, wantArray: true},
-		{name: "string", data: "foo", wantArray: false},
+		{name: "array", data: []interface{}{"foo", "123"}, wantArray: success},
+		{name: "string", data: "foo", wantArray: failure},
 	}
 
 	for _, tc := range cases {
 		t.Run(tc.name, func(t *testing.T) {
 			reporter := newMockReporter(t)
 
-			if tc.wantArray {
-				NewValue(reporter, tc.data).IsArray().
-					chain.assertNotFailed(t)
+			NewValue(reporter, tc.data).IsArray().
+				chain.assert(t, tc.wantArray)
 
-				NewValue(reporter, tc.data).NotArray().
-					chain.assertFailed(t)
-			} else {
-				NewValue(reporter, tc.data).NotArray().
-					chain.assertNotFailed(t)
-
-				NewValue(reporter, tc.data).IsArray().
-					chain.assertFailed(t)
-			}
+			NewValue(reporter, tc.data).NotArray().
+				chain.assert(t, !tc.wantArray)
 		})
 	}
 }
@@ -511,29 +485,21 @@ func TestValue_IsString(t *testing.T) {
 	cases := []struct {
 		name       string
 		data       interface{}
-		wantString bool
+		wantString chainResult
 	}{
-		{name: "string", data: "foo", wantString: true},
-		{name: "integer", data: 123, wantString: false},
+		{name: "string", data: "foo", wantString: success},
+		{name: "integer", data: 123, wantString: failure},
 	}
 
 	for _, tc := range cases {
 		t.Run(tc.name, func(t *testing.T) {
 			reporter := newMockReporter(t)
 
-			if tc.wantString {
-				NewValue(reporter, tc.data).IsString().
-					chain.assertNotFailed(t)
+			NewValue(reporter, tc.data).IsString().
+				chain.assert(t, tc.wantString)
 
-				NewValue(reporter, tc.data).NotString().
-					chain.assertFailed(t)
-			} else {
-				NewValue(reporter, tc.data).NotString().
-					chain.assertNotFailed(t)
-
-				NewValue(reporter, tc.data).IsString().
-					chain.assertFailed(t)
-			}
+			NewValue(reporter, tc.data).NotString().
+				chain.assert(t, !tc.wantString)
 		})
 	}
 }
@@ -542,29 +508,21 @@ func TestValue_IsNumber(t *testing.T) {
 	cases := []struct {
 		name       string
 		data       interface{}
-		wantNumber bool
+		wantNumber chainResult
 	}{
-		{name: "integer", data: 123, wantNumber: true},
-		{name: "string", data: "foo", wantNumber: false},
+		{name: "integer", data: 123, wantNumber: success},
+		{name: "string", data: "foo", wantNumber: failure},
 	}
 
 	for _, tc := range cases {
 		t.Run(tc.name, func(t *testing.T) {
 			reporter := newMockReporter(t)
 
-			if tc.wantNumber {
-				NewValue(reporter, tc.data).IsNumber().
-					chain.assertNotFailed(t)
+			NewValue(reporter, tc.data).IsNumber().
+				chain.assert(t, tc.wantNumber)
 
-				NewValue(reporter, tc.data).NotNumber().
-					chain.assertFailed(t)
-			} else {
-				NewValue(reporter, tc.data).NotNumber().
-					chain.assertNotFailed(t)
-
-				NewValue(reporter, tc.data).IsNumber().
-					chain.assertFailed(t)
-			}
+			NewValue(reporter, tc.data).NotNumber().
+				chain.assert(t, !tc.wantNumber)
 		})
 	}
 }
@@ -573,29 +531,21 @@ func TestValue_IsBoolean(t *testing.T) {
 	cases := []struct {
 		name     string
 		data     interface{}
-		wantBool bool
+		wantBool chainResult
 	}{
-		{name: "bool", data: true, wantBool: true},
-		{name: "string", data: "foo", wantBool: false},
+		{name: "bool", data: true, wantBool: success},
+		{name: "string", data: "foo", wantBool: failure},
 	}
 
 	for _, tc := range cases {
 		t.Run(tc.name, func(t *testing.T) {
 			reporter := newMockReporter(t)
 
-			if tc.wantBool {
-				NewValue(reporter, tc.data).IsBoolean().
-					chain.assertNotFailed(t)
-
-				NewValue(reporter, tc.data).NotBoolean().
-					chain.assertFailed(t)
-			} else {
-				NewValue(reporter, tc.data).NotBoolean().
-					chain.assertNotFailed(t)
+			NewValue(reporter, tc.data).IsBoolean().
+				chain.assert(t, tc.wantBool)
 
-				NewValue(reporter, tc.data).IsBoolean().
-					chain.assertFailed(t)
-			}
+			NewValue(reporter, tc.data).NotBoolean().
+				chain.assert(t, !tc.wantBool)
 		})
 	}
 }
@@ -606,25 +556,25 @@ func TestValue_IsEqual(t *testing.T) {
 	data1 := map[string]interface{}{"foo": "bar"}
 	data2 := "baz"
 
-	NewValue(reporter, data1).IsEqual(data1).chain.assertNotFailed(t)
-	NewValue(reporter, data2).IsEqual(data2).chain.assertNotFailed(t)
+	NewValue(reporter, data1).IsEqual(data1).chain.assert(t, success)
+	NewValue(reporter, data2).IsEqual(data2).chain.assert(t, success)
 
-	NewValue(reporter, data1).NotEqual(data1).chain.assertFailed(t)
-	NewValue(reporter, data2).NotEqual(data2).chain.assertFailed(t)
+	NewValue(reporter, data1).NotEqual(data1).chain.assert(t, failure)
+	NewValue(reporter, data2).NotEqual(data2).chain.assert(t, failure)
 
-	NewValue(reporter, data1).IsEqual(data2).chain.assertFailed(t)
-	NewValue(reporter, data2).IsEqual(data1).chain.assertFailed(t)
+	NewValue(reporter, data1).IsEqual(data2).chain.assert(t, failure)
+	NewValue(reporter, data2).IsEqual(data1).chain.assert(t, failure)
 
-	NewValue(reporter, data1).NotEqual(data2).chain.assertNotFailed(t)
-	NewValue(reporter, data2).NotEqual(data1).chain.assertNotFailed(t)
+	NewValue(reporter, data1).NotEqual(data2).chain.assert(t, success)
+	NewValue(reporter, data2).NotEqual(data1).chain.assert(t, success)
 
-	NewValue(reporter, nil).IsEqual(nil).chain.assertNotFailed(t)
+	NewValue(reporter, nil).IsEqual(nil).chain.assert(t, success)
 
-	NewValue(reporter, nil).IsEqual(map[string]interface{}(nil)).chain.assertNotFailed(t)
-	NewValue(reporter, nil).IsEqual(map[string]interface{}{}).chain.assertFailed(t)
+	NewValue(reporter, nil).IsEqual(map[string]interface{}(nil)).chain.assert(t, success)
+	NewValue(reporter, nil).IsEqual(map[string]interface{}{}).chain.assert(t, failure)
 
-	NewValue(reporter, data1).IsEqual(func() {}).chain.assertFailed(t)
-	NewValue(reporter, data1).NotEqual(func() {}).chain.assertFailed(t)
+	NewValue(reporter, data1).IsEqual(func() {}).chain.assert(t, failure)
+	NewValue(reporter, data1).NotEqual(func() {}).chain.assert(t, failure)
 }
 
 func TestValue_InList(t *testing.T) {
@@ -638,32 +588,32 @@ func TestValue_InList(t *testing.T) {
 		Data: []int{1, 2, 3, 4},
 	}
 
-	NewValue(reporter, data1).InList().chain.assertFailed(t)
-	NewValue(reporter, data2).NotInList().chain.assertFailed(t)
+	NewValue(reporter, data1).InList().chain.assert(t, failure)
+	NewValue(reporter, data2).NotInList().chain.assert(t, failure)
 
-	NewValue(reporter, data1).InList(data1, data3).chain.assertNotFailed(t)
-	NewValue(reporter, data2).NotInList(data1, data3).chain.assertNotFailed(t)
+	NewValue(reporter, data1).InList(data1, data3).chain.assert(t, success)
+	NewValue(reporter, data2).NotInList(data1, data3).chain.assert(t, success)
 
-	NewValue(reporter, data1).InList(data2, data3).chain.assertFailed(t)
-	NewValue(reporter, data2).NotInList(data2, data3).chain.assertFailed(t)
+	NewValue(reporter, data1).InList(data2, data3).chain.assert(t, failure)
+	NewValue(reporter, data2).NotInList(data2, data3).chain.assert(t, failure)
 
-	NewValue(reporter, data1).InList(data2).chain.assertFailed(t)
-	NewValue(reporter, data2).NotInList(data2).chain.assertFailed(t)
+	NewValue(reporter, data1).InList(data2).chain.assert(t, failure)
+	NewValue(reporter, data2).NotInList(data2).chain.assert(t, failure)
 
-	NewValue(reporter, data1).InList(data1).chain.assertNotFailed(t)
-	NewValue(reporter, data2).NotInList(data1).chain.assertNotFailed(t)
+	NewValue(reporter, data1).InList(data1).chain.assert(t, success)
+	NewValue(reporter, data2).NotInList(data1).chain.assert(t, success)
 
-	NewValue(reporter, nil).InList(map[string]interface{}(nil)).chain.assertNotFailed(t)
-	NewValue(reporter, nil).NotInList(map[string]interface{}{}).chain.assertNotFailed(t)
+	NewValue(reporter, nil).InList(map[string]interface{}(nil)).chain.assert(t, success)
+	NewValue(reporter, nil).NotInList(map[string]interface{}{}).chain.assert(t, success)
 
-	NewValue(reporter, data1).InList(func() {}).chain.assertFailed(t)
-	NewValue(reporter, data1).NotInList(func() {}).chain.assertFailed(t)
+	NewValue(reporter, data1).InList(func() {}).chain.assert(t, failure)
+	NewValue(reporter, data1).NotInList(func() {}).chain.assert(t, failure)
 
-	NewValue(reporter, data1).InList(data1, func() {}).chain.assertFailed(t)
-	NewValue(reporter, data1).NotInList(data1, func() {}).chain.assertFailed(t)
+	NewValue(reporter, data1).InList(data1, func() {}).chain.assert(t, failure)
+	NewValue(reporter, data1).NotInList(data1, func() {}).chain.assert(t, failure)
 
-	NewValue(reporter, data1).InList(data2, func() {}).chain.assertFailed(t)
-	NewValue(reporter, data1).NotInList(data2, func() {}).chain.assertFailed(t)
+	NewValue(reporter, data1).InList(data2, func() {}).chain.assert(t, failure)
+	NewValue(reporter, data1).NotInList(data2, func() {}).chain.assert(t, failure)
 }
 
 func TestValue_PathTypes(t *testing.T) {
@@ -680,28 +630,40 @@ func TestValue_PathTypes(t *testing.T) {
 			},
 		}
 
-		value := NewValue(reporter, data)
+		t.Run("queries", func(t *testing.T) {
+			value := NewValue(reporter, data)
+
+			assert.Equal(t, data, value.Path("$").Raw())
+			assert.Equal(t, data["users"], value.Path("$.users").Raw())
+			assert.Equal(t, user0, value.Path("$.users[0]").Raw())
+			assert.Equal(t, "john", value.Path("$.users[0].name").Raw())
+			assert.Equal(t, []interface{}{"john", "bob"}, value.Path("$.users[*].name").Raw())
+			assert.Equal(t, []interface{}{"john", "bob"}, value.Path("$..name").Raw())
+			value.chain.assert(t, success)
+
+			names := value.Path("$..name").Array().Iter()
+			names[0].String().IsEqual("john").chain.assert(t, success)
+			names[1].String().IsEqual("bob").chain.assert(t, success)
+			value.chain.assert(t, success)
+		})
 
-		assert.Equal(t, data, value.Path("$").Raw())
-		assert.Equal(t, data["users"], value.Path("$.users").Raw())
-		assert.Equal(t, user0, value.Path("$.users[0]").Raw())
-		assert.Equal(t, "john", value.Path("$.users[0].name").Raw())
-		assert.Equal(t, []interface{}{"john", "bob"}, value.Path("$.users[*].name").Raw())
-		assert.Equal(t, []interface{}{"john", "bob"}, value.Path("$..name").Raw())
-		value.chain.assertNotFailed(t)
+		t.Run("bad key", func(t *testing.T) {
+			value := NewValue(reporter, data)
 
-		names := value.Path("$..name").Array().Iter()
-		names[0].String().IsEqual("john").chain.assertNotFailed(t)
-		names[1].String().IsEqual("bob").chain.assertNotFailed(t)
-		value.chain.assertNotFailed(t)
+			bad := value.Path("$.bad")
+			assert.True(t, bad != nil)
+			assert.True(t, bad.Raw() == nil)
+			value.chain.assert(t, failure)
+		})
 
-		for _, key := range []string{"$.bad", "!"} {
-			bad := value.Path(key)
+		t.Run("invalid query", func(t *testing.T) {
+			value := NewValue(reporter, data)
+
+			bad := value.Path("!")
 			assert.True(t, bad != nil)
 			assert.True(t, bad.Raw() == nil)
-			value.chain.assertFailed(t)
-			value.chain.clearFailed()
-		}
+			value.chain.assert(t, failure)
+		})
 	})
 
 	t.Run("array", func(t *testing.T) {
@@ -720,7 +682,7 @@ func TestValue_PathTypes(t *testing.T) {
 		assert.Equal(t, "john", value.Path("$[0].name").Raw())
 		assert.Equal(t, []interface{}{"john", "bob"}, value.Path("$[*].name").Raw())
 		assert.Equal(t, []interface{}{"john", "bob"}, value.Path("$..name").Raw())
-		value.chain.assertNotFailed(t)
+		value.chain.assert(t, success)
 	})
 
 	t.Run("string", func(t *testing.T) {
@@ -729,7 +691,7 @@ func TestValue_PathTypes(t *testing.T) {
 		value := NewValue(reporter, data)
 
 		assert.Equal(t, data, value.Path("$").Raw())
-		value.chain.assertNotFailed(t)
+		value.chain.assert(t, success)
 	})
 
 	t.Run("number", func(t *testing.T) {
@@ -738,7 +700,7 @@ func TestValue_PathTypes(t *testing.T) {
 		value := NewValue(reporter, data)
 
 		assert.Equal(t, float64(data), value.Path("$").Raw())
-		value.chain.assertNotFailed(t)
+		value.chain.assert(t, success)
 	})
 
 	t.Run("boolean", func(t *testing.T) {
@@ -747,14 +709,14 @@ func TestValue_PathTypes(t *testing.T) {
 		value := NewValue(reporter, data)
 
 		assert.Equal(t, data, value.Path("$").Raw())
-		value.chain.assertNotFailed(t)
+		value.chain.assert(t, success)
 	})
 
 	t.Run("null", func(t *testing.T) {
 		value := NewValue(reporter, nil)
 
 		assert.Equal(t, nil, value.Path("$").Raw())
-		value.chain.assertNotFailed(t)
+		value.chain.assert(t, success)
 	})
 
 	t.Run("error", func(t *testing.T) {
@@ -766,7 +728,7 @@ func TestValue_PathTypes(t *testing.T) {
 			bad := value.Path(key)
 			assert.True(t, bad != nil)
 			assert.True(t, bad.Raw() == nil)
-			value.chain.assertFailed(t)
+			value.chain.assert(t, failure)
 		}
 	})
 
@@ -777,14 +739,14 @@ func TestValue_PathTypes(t *testing.T) {
 		}
 
 		value := NewValue(reporter, data)
-		value.chain.assertNotFailed(t)
+		value.chain.assert(t, success)
 
 		a := value.Path(`$["A"]`)
-		a.chain.assertNotFailed(t)
+		a.chain.assert(t, success)
 		assert.Equal(t, 123.0, a.Raw())
 
 		b := value.Path(`$["B"]`)
-		b.chain.assertNotFailed(t)
+		b.chain.assert(t, success)
 		assert.Equal(t, 123.0, b.Raw())
 	})
 }
@@ -846,11 +808,11 @@ func TestValue_PathExpressions(t *testing.T) {
 
 	runTests := func(tests map[string]interface{}) {
 		value := NewValue(reporter, data)
-		value.chain.assertNotFailed(t)
+		value.chain.assert(t, success)
 
 		for path, expected := range tests {
 			actual := value.Path(path)
-			actual.chain.assertNotFailed(t)
+			actual.chain.assert(t, success)
 
 			assert.Equal(t, expected, actual.Raw())
 		}
@@ -996,18 +958,18 @@ func TestValue_Schema(t *testing.T) {
 		"bar": "b",
 	}
 
-	NewValue(reporter, data1).Schema(schema).chain.assertNotFailed(t)
-	NewValue(reporter, data2).Schema(schema).chain.assertFailed(t)
+	NewValue(reporter, data1).Schema(schema).chain.assert(t, success)
+	NewValue(reporter, data2).Schema(schema).chain.assert(t, failure)
 
-	NewValue(reporter, data1).Schema([]byte(schema)).chain.assertNotFailed(t)
-	NewValue(reporter, data2).Schema([]byte(schema)).chain.assertFailed(t)
+	NewValue(reporter, data1).Schema([]byte(schema)).chain.assert(t, success)
+	NewValue(reporter, data2).Schema([]byte(schema)).chain.assert(t, failure)
 
 	var b interface{}
 	err := json.Unmarshal([]byte(schema), &b)
 	require.Nil(t, err)
 
-	NewValue(reporter, data1).Schema(b).chain.assertNotFailed(t)
-	NewValue(reporter, data2).Schema(b).chain.assertFailed(t)
+	NewValue(reporter, data1).Schema(b).chain.assert(t, success)
+	NewValue(reporter, data2).Schema(b).chain.assert(t, failure)
 
 	tmp, _ := ioutil.TempFile("", "httpexpect")
 	defer os.Remove(tmp.Name())
@@ -1020,9 +982,9 @@ func TestValue_Schema(t *testing.T) {
 
 	url := "file://" + tmp.Name()
 
-	NewValue(reporter, data1).Schema(url).chain.assertNotFailed(t)
-	NewValue(reporter, data2).Schema(url).chain.assertFailed(t)
+	NewValue(reporter, data1).Schema(url).chain.assert(t, success)
+	NewValue(reporter, data2).Schema(url).chain.assert(t, failure)
 
-	NewValue(reporter, data1).Schema("file:///bad/path").chain.assertFailed(t)
-	NewValue(reporter, data1).Schema("{ bad json").chain.assertFailed(t)
+	NewValue(reporter, data1).Schema("file:///bad/path").chain.assert(t, failure)
+	NewValue(reporter, data1).Schema("{ bad json").chain.assert(t, failure)
 }