Skip to content
This repository has been archived by the owner on Jun 27, 2023. It is now read-only.

gives full control on both the Want and Got field formatting #236

Merged
merged 2 commits into from
Nov 27, 2019
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
47 changes: 47 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,53 @@ func TestFoo(t *testing.T) {
}
```

### Modifying Failure Messages

When a matcher reports a failure, it prints the received (`Got`) vs the
expected (`Want`) value.

```
Got: [3]
Want: is equal to 2
Expected call at user_test.go:33 doesn't match the argument at index 1.
Got: [0 1 1 2 3]
Want: is equal to 1
```

##### Modifying `Want`

The `Want` value comes from the matcher's `String()` method. If the matcher's
default output doesn't meet your needs, then it can be modified as follows:

```go
gomock.WantFormatter(
gomock.StringerFunc(func() string { return "is equal to fifteen" }),
gomock.Eq(15),
)
```

This modifies the `gomock.Eq(15)` matcher's output for `Want:` from `is equal
to 15` to `is equal to fifteen`.

##### Modifying `Got`

The `Got` value comes from the object's `String()` method if it is available.
In some cases the output of an object is difficult to read (e.g., `[]byte`) and
it would be helpful for the test to print it differently. The following
modifies how the `Got` value is formatted:

```go
gomock.GotFormatterAdapter(
gomock.GotFormatterFunc(func(i interface{}) string {
// Leading 0s
return fmt.Sprintf("%02d", i)
}),
gomock.Eq(15),
)
```

If the received value is `3`, then it will be printed as `03`.

[golang]: http://golang.org/
[golang-install]: http://golang.org/doc/install.html#releases
[gomock-ref]: http://godoc.org/github.com/golang/mock/gomock
Expand Down
11 changes: 9 additions & 2 deletions gomock/call.go
Original file line number Diff line number Diff line change
Expand Up @@ -301,8 +301,15 @@ func (c *Call) matches(args []interface{}) error {

for i, m := range c.args {
if !m.Matches(args[i]) {
return fmt.Errorf("Expected call at %s doesn't match the argument at index %s.\nGot: %v\nWant: %v",
c.origin, strconv.Itoa(i), args[i], m)
got := fmt.Sprintf("%v", args[i])
if gs, ok := m.(GotFormatter); ok {
got = gs.Got(args[i])
}

return fmt.Errorf(
"Expected call at %s doesn't match the argument at index %d.\nGot: %v\nWant: %v",
c.origin, i, got, m,
)
}
}
} else {
Expand Down
57 changes: 57 additions & 0 deletions gomock/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,63 @@ func TestUnexpectedArgValue_SecondArg(t *testing.T) {
})
}

func TestUnexpectedArgValue_WantFormatter(t *testing.T) {
reporter, ctrl := createFixtures(t)
defer reporter.recoverUnexpectedFatal()
subject := new(Subject)

expectedArg0 := TestStruct{Number: 123, Message: "hello"}
ctrl.RecordCall(
subject,
"ActOnTestStructMethod",
expectedArg0,
gomock.WantFormatter(
gomock.StringerFunc(func() string { return "is equal to fifteen" }),
gomock.Eq(15),
),
)

reporter.assertFatal(func() {
ctrl.Call(subject, "ActOnTestStructMethod", TestStruct{Number: 123, Message: "hello"}, 3)
}, "Unexpected call to", "doesn't match the argument at index 1",
"Got: 3\nWant: is equal to fifteen")

reporter.assertFatal(func() {
// The expected call wasn't made.
ctrl.Finish()
})
}

func TestUnexpectedArgValue_GotFormatter(t *testing.T) {
reporter, ctrl := createFixtures(t)
defer reporter.recoverUnexpectedFatal()
subject := new(Subject)

expectedArg0 := TestStruct{Number: 123, Message: "hello"}
ctrl.RecordCall(
subject,
"ActOnTestStructMethod",
expectedArg0,
gomock.GotFormatterAdapter(
gomock.GotFormatterFunc(func(i interface{}) string {
// Leading 0s
return fmt.Sprintf("%02d", i)
}),
gomock.Eq(15),
),
)

reporter.assertFatal(func() {
ctrl.Call(subject, "ActOnTestStructMethod", TestStruct{Number: 123, Message: "hello"}, 3)
}, "Unexpected call to", "doesn't match the argument at index 1",
"Got: 03\nWant: is equal to 15")

reporter.assertFatal(func() {
// The expected call wasn't made.
ctrl.Finish()
})
}

func TestAnyTimes(t *testing.T) {
reporter, ctrl := createFixtures(t)
subject := new(Subject)
Expand Down
57 changes: 57 additions & 0 deletions gomock/matchers.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,63 @@ type Matcher interface {
String() string
}

// WantFormatter modifies the given Matcher's String() method to the given
// Stringer. This allows for control on how the "Want" is formatted when
// printing .
func WantFormatter(s fmt.Stringer, m Matcher) Matcher {
type matcher interface {
Matches(x interface{}) bool
}

return struct {
matcher
fmt.Stringer
}{
matcher: m,
Stringer: s,
}
}

// StringerFunc type is an adapter to allow the use of ordinary functions as
// a Stringer. If f is a function with the appropriate signature,
// StringerFunc(f) is a Stringer that calls f.
type StringerFunc func() string

// String implements fmt.Stringer.
func (f StringerFunc) String() string {
return f()
}

// GotFormatter is used to better print failure messages. If a matcher
// implements GotFormatter, it will use the result from Got when printing
// the failure message.
type GotFormatter interface {
// Got is invoked with the received value. The result is used when
// printing the failure message.
Got(got interface{}) string
}

// GotFormatterFunc type is an adapter to allow the use of ordinary
// functions as a GotFormatter. If f is a function with the appropriate
// signature, GotFormatterFunc(f) is a GotFormatter that calls f.
type GotFormatterFunc func(got interface{}) string

// Got implements GotFormatter.
func (f GotFormatterFunc) Got(got interface{}) string {
return f(got)
}

// GotFormatterAdapter attaches a GotFormatter to a Matcher.
func GotFormatterAdapter(s GotFormatter, m Matcher) Matcher {
return struct {
GotFormatter
Matcher
}{
GotFormatter: s,
Matcher: m,
}
}

type anyMatcher struct{}

func (anyMatcher) Matches(x interface{}) bool {
Expand Down