Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement PanicWith(). #381

Merged
merged 2 commits into from
May 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 10 additions & 0 deletions matchers.go
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,16 @@ func Panic() types.GomegaMatcher {
return &matchers.PanicMatcher{}
}

//PanicWith succeeds if actual is a function that, when invoked, panics with a specific value.
//Actual must be a function that takes no arguments and returns no results.
//
//By default PanicWith uses Equal() to perform the match, however a
//matcher can be passed in instead:
// Expect(fn).Should(PanicWith(MatchRegexp(`.+Foo$`)))
func PanicWith(expected interface{}) types.GomegaMatcher {
return &matchers.PanicMatcher{Expected: expected}
}

//BeAnExistingFile succeeds if a file exists.
//Actual must be a string representing the abs path to the file being checked.
func BeAnExistingFile() types.GomegaMatcher {
Expand Down
76 changes: 72 additions & 4 deletions matchers/panic_matcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import (
)

type PanicMatcher struct {
object interface{}
Expected interface{}
object interface{}
}

func (matcher *PanicMatcher) Match(actual interface{}) (success bool, err error) {
Expand All @@ -28,7 +29,21 @@ func (matcher *PanicMatcher) Match(actual interface{}) (success bool, err error)
defer func() {
if e := recover(); e != nil {
matcher.object = e
success = true

if matcher.Expected == nil {
success = true
return
}

valueMatcher, valueIsMatcher := matcher.Expected.(omegaMatcher)
if !valueIsMatcher {
valueMatcher = &EqualMatcher{Expected: matcher.Expected}
}

success, err = valueMatcher.Match(e)
if err != nil {
err = fmt.Errorf("PanicMatcher's value matcher failed with:\n%s%s", format.Indent, err.Error())
}
}
}()

Expand All @@ -38,9 +53,62 @@ func (matcher *PanicMatcher) Match(actual interface{}) (success bool, err error)
}

func (matcher *PanicMatcher) FailureMessage(actual interface{}) (message string) {
return format.Message(actual, "to panic")
if matcher.Expected == nil {
// We wanted any panic to occur, but none did.
return format.Message(actual, "to panic")
}

if matcher.object == nil {
// We wanted a panic with a specific value to occur, but none did.
switch matcher.Expected.(type) {
case omegaMatcher:
return format.Message(actual, "to panic with a value matching", matcher.Expected)
default:
return format.Message(actual, "to panic with", matcher.Expected)
}
}

// We got a panic, but the value isn't what we expected.
switch matcher.Expected.(type) {
case omegaMatcher:
return format.Message(
actual,
fmt.Sprintf(
"to panic with a value matching\n%s\nbut panicked with\n%s",
format.Object(matcher.Expected, 1),
format.Object(matcher.object, 1),
),
)
default:
return format.Message(
actual,
fmt.Sprintf(
"to panic with\n%s\nbut panicked with\n%s",
format.Object(matcher.Expected, 1),
format.Object(matcher.object, 1),
),
)
}
}

func (matcher *PanicMatcher) NegatedFailureMessage(actual interface{}) (message string) {
return format.Message(actual, fmt.Sprintf("not to panic, but panicked with\n%s", format.Object(matcher.object, 1)))
if matcher.Expected == nil {
// We didn't want any panic to occur, but one did.
return format.Message(actual, fmt.Sprintf("not to panic, but panicked with\n%s", format.Object(matcher.object, 1)))
}

// We wanted a to ensure a panic with a specific value did not occur, but it did.
switch matcher.Expected.(type) {
case omegaMatcher:
return format.Message(
actual,
fmt.Sprintf(
"not to panic with a value matching\n%s\nbut panicked with\n%s",
format.Object(matcher.Expected, 1),
format.Object(matcher.object, 1),
),
)
default:
return format.Message(actual, "not to panic with", matcher.Expected)
}
}
114 changes: 113 additions & 1 deletion matchers/panic_matcher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ var _ = Describe("Panic", func() {
})

When("assertion fails", func() {
It("prints the object passed to Panic when negative", func() {
It("prints the object passed to panic() when negative", func() {
failuresMessages := InterceptGomegaFailures(func() {
Expect(func() { panic("ack!") }).NotTo(Panic())
})
Expand All @@ -50,3 +50,115 @@ var _ = Describe("Panic", func() {
})
})
})

var _ = Describe("PanicWith", func() {
When("a specific panic value is expected", func() {
matcher := PanicWith("ack!")

When("no panic occurs", func() {
actual := func() {}

It("prints a message that includes the expected value", func() {
failuresMessages := InterceptGomegaFailures(func() {
Expect(actual).To(matcher)
})
Expect(failuresMessages).To(ConsistOf(
MatchRegexp("Expected\n\\s+<func\\(\\)>: .+\nto panic with\\s+<string>: ack!"),
))
})

It("passes when negated", func() {
Expect(actual).NotTo(matcher)
})
})

When("the panic value matches", func() {
actual := func() { panic("ack!") }

It("passes", func() {
Expect(actual).To(matcher)
})

It("prints a message that includes the (un)expected value when negated", func() {
failuresMessages := InterceptGomegaFailures(func() {
Expect(actual).NotTo(matcher)
})
Expect(failuresMessages).To(ConsistOf(
MatchRegexp("Expected\n\\s+<func\\(\\)>: .+\nnot to panic with\\s+<string>: ack!"),
))
})
})

When("the panic value does not match", func() {
actual := func() { panic("unexpected!") }

It("prints a message that includes both the actual and expected values", func() {
failuresMessages := InterceptGomegaFailures(func() {
Expect(actual).To(matcher)
})
Expect(failuresMessages).To(ConsistOf(
MatchRegexp("Expected\n\\s+<func\\(\\)>: .+\nto panic with\\s+<string>: ack!\nbut panicked with\n\\s+<string>: unexpected!"),
))
})

It("passes when negated", func() {
Expect(actual).NotTo(matcher)
})
})
})

When("the expected value is actually a matcher", func() {
matcher := PanicWith(MatchRegexp("ack"))

When("no panic occurs", func() {
actual := func() {}

It("prints a message that includes the expected value", func() {
failuresMessages := InterceptGomegaFailures(func() {
Expect(actual).To(matcher)
})
Expect(failuresMessages).To(ConsistOf(
MatchRegexp("Expected\n\\s+<func\\(\\)>: .+\nto panic with a value matching\n.+MatchRegexpMatcher.+ack"),
))
})

It("passes when negated", func() {
Expect(actual).NotTo(matcher)
})
})

When("the panic value matches", func() {
actual := func() { panic("ack!") }

It("passes", func() {
Expect(actual).To(matcher)
})

It("prints a message that includes the (un)expected value when negated", func() {
failuresMessages := InterceptGomegaFailures(func() {
Expect(actual).NotTo(matcher)
})
Expect(failuresMessages).To(ConsistOf(
MatchRegexp("Expected\n\\s+<func\\(\\)>: .+\nnot to panic with a value matching\n.+MatchRegexpMatcher.+ack"),
))
})
})

When("the panic value does not match", func() {
actual := func() { panic("unexpected!") }

It("prints a message that includes both the actual and expected values", func() {
failuresMessages := InterceptGomegaFailures(func() {
Expect(actual).To(matcher)
})
Expect(failuresMessages).To(ConsistOf(
MatchRegexp("Expected\n\\s+<func\\(\\)>: .+\nto panic with a value matching\n.+MatchRegexpMatcher.+ack.+\nbut panicked with\n\\s+<string>: unexpected!"),
))
})

It("passes when negated", func() {
Expect(actual).NotTo(matcher)
})
})
})
})