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

Commit

Permalink
Adds GotFormatter interface and WantFormatter func (#236)
Browse files Browse the repository at this point in the history
- WantFormatter is used to modify a Matcher's String() method.
- GotFormatter is used to format the Got value for printing a failure message.
  • Loading branch information
poy authored and codyoss committed Nov 27, 2019
1 parent 3fae808 commit 112dfb8
Show file tree
Hide file tree
Showing 4 changed files with 170 additions and 2 deletions.
47 changes: 47 additions & 0 deletions README.md
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
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
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
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

0 comments on commit 112dfb8

Please sign in to comment.