1 package matchers_test
2
3 import (
4 "errors"
5 "fmt"
6
7 . "github.com/onsi/ginkgo/v2"
8 . "github.com/onsi/gomega"
9 . "github.com/onsi/gomega/matchers"
10 )
11
12 type CustomError struct {
13 }
14
15 func (c CustomError) Error() string {
16 return "an error"
17 }
18
19 type ComplexError struct {
20 Key string
21 }
22
23 func (t *ComplexError) Error() string {
24 return fmt.Sprintf("err: %s", t.Key)
25 }
26
27 var _ = Describe("MatchErrorMatcher", func() {
28 Context("When asserting against an error", func() {
29 When("passed an error", func() {
30 It("should succeed when errors are deeply equal", func() {
31 err := errors.New("an error")
32 fmtErr := fmt.Errorf("an error")
33 customErr := CustomError{}
34
35 Expect(err).Should(MatchError(errors.New("an error")))
36 Expect(err).ShouldNot(MatchError(errors.New("another error")))
37
38 Expect(fmtErr).Should(MatchError(errors.New("an error")))
39 Expect(customErr).Should(MatchError(CustomError{}))
40 })
41
42 It("should succeed when any error in the chain matches the passed error", func() {
43 innerErr := errors.New("inner error")
44 outerErr := fmt.Errorf("outer error wrapping: %w", innerErr)
45
46 Expect(outerErr).Should(MatchError(innerErr))
47 })
48
49 It("uses deep equality with unwrapped errors", func() {
50 innerErr := &ComplexError{Key: "abc"}
51 outerErr := fmt.Errorf("outer error wrapping: %w", &ComplexError{Key: "abc"})
52 Expect(outerErr).To(MatchError(innerErr))
53 })
54 })
55
56 When("actual an expected are both pointers to an error", func() {
57 It("should succeed when errors are deeply equal", func() {
58 err := CustomError{}
59 Expect(&err).To(MatchError(&err))
60 })
61 })
62
63 It("should succeed when matching with a string", func() {
64 err := errors.New("an error")
65 fmtErr := fmt.Errorf("an error")
66 customErr := CustomError{}
67
68 Expect(err).Should(MatchError("an error"))
69 Expect(err).ShouldNot(MatchError("another error"))
70
71 Expect(fmtErr).Should(MatchError("an error"))
72 Expect(customErr).Should(MatchError("an error"))
73 })
74
75 When("passed a matcher", func() {
76 It("should pass if the matcher passes against the error string", func() {
77 err := errors.New("error 123 abc")
78
79 Expect(err).Should(MatchError(MatchRegexp(`\d{3}`)))
80 })
81
82 It("should fail if the matcher fails against the error string", func() {
83 err := errors.New("no digits")
84 Expect(err).ShouldNot(MatchError(MatchRegexp(`\d`)))
85 })
86 })
87
88 When("passed a function that takes error and returns bool", func() {
89 var IsFooError = func(err error) bool {
90 return err.Error() == "foo"
91 }
92
93 It("requires an additional description", func() {
94 _, err := (&MatchErrorMatcher{
95 Expected: IsFooError,
96 }).Match(errors.New("foo"))
97 Expect(err).Should(MatchError("MatchError requires an additional description when passed a function"))
98 })
99
100 It("matches iff the function returns true", func() {
101 Ω(errors.New("foo")).Should(MatchError(IsFooError, "FooError"))
102 Ω(errors.New("fooo")).ShouldNot(MatchError(IsFooError, "FooError"))
103 })
104
105 It("uses the error description to construct its message", func() {
106 failuresMessages := InterceptGomegaFailures(func() {
107 Ω(errors.New("fooo")).Should(MatchError(IsFooError, "FooError"))
108 })
109 Ω(failuresMessages[0]).Should(ContainSubstring("fooo\n {s: \"fooo\"}\nto match error function FooError"))
110
111 failuresMessages = InterceptGomegaFailures(func() {
112 Ω(errors.New("foo")).ShouldNot(MatchError(IsFooError, "FooError"))
113 })
114 Ω(failuresMessages[0]).Should(ContainSubstring("foo\n {s: \"foo\"}\nnot to match error function FooError"))
115 })
116 })
117
118 It("should fail when passed anything else", func() {
119 actualErr := errors.New("an error")
120 _, err := (&MatchErrorMatcher{
121 Expected: []byte("an error"),
122 }).Match(actualErr)
123 Expect(err).Should(HaveOccurred())
124
125 _, err = (&MatchErrorMatcher{
126 Expected: 3,
127 }).Match(actualErr)
128 Expect(err).Should(HaveOccurred())
129
130 _, err = (&MatchErrorMatcher{
131 Expected: func(e error) {},
132 }).Match(actualErr)
133 Expect(err).Should(HaveOccurred())
134
135 _, err = (&MatchErrorMatcher{
136 Expected: func() bool { return false },
137 }).Match(actualErr)
138 Expect(err).Should(HaveOccurred())
139
140 _, err = (&MatchErrorMatcher{
141 Expected: func() {},
142 }).Match(actualErr)
143 Expect(err).Should(HaveOccurred())
144
145 _, err = (&MatchErrorMatcher{
146 Expected: func(e error, a string) (bool, error) { return false, nil },
147 }).Match(actualErr)
148 Expect(err).Should(HaveOccurred())
149 })
150 })
151
152 When("passed nil", func() {
153 It("should fail", func() {
154 _, err := (&MatchErrorMatcher{
155 Expected: "an error",
156 }).Match(nil)
157 Expect(err).Should(HaveOccurred())
158 })
159 })
160
161 When("passed a non-error", func() {
162 It("should fail", func() {
163 _, err := (&MatchErrorMatcher{
164 Expected: "an error",
165 }).Match("an error")
166 Expect(err).Should(HaveOccurred())
167
168 _, err = (&MatchErrorMatcher{
169 Expected: "an error",
170 }).Match(3)
171 Expect(err).Should(HaveOccurred())
172 })
173 })
174
175 When("passed an error that is also a string", func() {
176 It("should use it as an error", func() {
177 var e mockErr = "mockErr"
178
179
180 Expect(e).Should(MatchError(e))
181 })
182 })
183
184 It("shows failure message", func() {
185 failuresMessages := InterceptGomegaFailures(func() {
186 Expect(errors.New("foo")).To(MatchError("bar"))
187 })
188 Expect(failuresMessages[0]).To(ContainSubstring("foo\n {s: \"foo\"}\nto match error\n <string>: bar"))
189 })
190
191 It("shows negated failure message", func() {
192 failuresMessages := InterceptGomegaFailures(func() {
193 Expect(errors.New("foo")).ToNot(MatchError("foo"))
194 })
195 Expect(failuresMessages[0]).To(ContainSubstring("foo\n {s: \"foo\"}\nnot to match error\n <string>: foo"))
196 })
197
198 })
199
200 type mockErr string
201
202 func (m mockErr) Error() string { return string(m) }
203
View as plain text