Issue with Matcher I do not understand (AssignableTo returns unexpected false)
Closed this issue · 3 comments
I am building a REST endpoint using gorilla/mux
. I am passing an object into my http.Handlerfunc
which will get enriched with the request's context when the endpoint is called.
Now the code sample at the bottom reproduces the issue. I also added a Println to matcher.go:
func (matcher *AnyMatcher) Matches(param Param) bool {
matcher.actual = reflect.TypeOf(param)
if matcher.actual == nil {
switch matcher.Type.Kind() {
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map,
reflect.Ptr, reflect.Slice, reflect.UnsafePointer:
return true
default:
return false
}
}
fmt.Printf("Matcher Type: %v - Param Type: %v -> matches %v\n", matcher.Type, matcher.actual, matcher.actual.AssignableTo(matcher.Type) )
return matcher.actual.AssignableTo(matcher.Type)
}
The output, when I run the sample code below, is as follows:
Matcher Type: *context.valueCtx - Param Type: *context.valueCtx -> matches false
Shouldn't be nil <nil>
What I do not understand is: The types are the same, but AssignableTo
returns false. I guess I am making a wrong assumption about reflect.TypeOf
because my understanding is: If I reflect.TypeOf
and object, the type should be completely independent from the content of the object. So why would the code fail? I am clueless.
The sample code:
package main
import(
"github.com/petergtz/pegomock"
"golang.org/x/net/context"
"net/http"
"fmt"
"reflect"
"net/http/httptest"
"github.com/gorilla/mux"
)
func main() {
//Create mock
mockClient := NewMockSomeClient()
//Return mock when Withontext is called with any context (`mux` creates a *context.valueCtx so does the Matcher)
pegomock.When(mockClient.WithContext(AnyContext())).ThenReturn(mockClient)
responseRecorder := httptest.NewRecorder()
request, _ := http.NewRequest("GET", "/path/to/success", nil)
r := mux.NewRouter()
r.HandleFunc("/path/to/success", NewEndpoint(mockClient))
r.ServeHTTP(responseRecorder, request)
}
func AnyContext() context.Context {
ctx := context.WithValue(context.Background(), "", "")
pegomock.RegisterMatcher(pegomock.NewAnyMatcher(reflect.TypeOf(ctx)))
return ctx
}
func NewEndpoint(client SomeClient) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request){
clientWithContext := client.WithContext(r.Context())
fmt.Printf("Shouldn't be nil %v", clientWithContext)
}
}
//Interface to mock
type SomeClient interface {
WithContext(context.Context) SomeClient
}
//MOCK - Generated but package adjusted, so it can be in the same file
type MockSomeClient struct {
fail func(message string, callerSkip ...int)
}
func NewMockSomeClient() *MockSomeClient {
return &MockSomeClient{fail: pegomock.GlobalFailHandler}
}
func (mock *MockSomeClient) WithContext(_param0 context.Context) SomeClient {
params := []pegomock.Param{_param0}
result := pegomock.GetGenericMockFrom(mock).Invoke("WithContext", params, []reflect.Type{reflect.TypeOf((*SomeClient)(nil)).Elem()})
var ret0 SomeClient
if len(result) != 0 {
if result[0] != nil {
ret0 = result[0].(SomeClient)
}
}
return ret0
}
func (mock *MockSomeClient) VerifyWasCalledOnce() *VerifierSomeClient {
return &VerifierSomeClient{mock, pegomock.Times(1), nil}
}
func (mock *MockSomeClient) VerifyWasCalled(invocationCountMatcher pegomock.Matcher) *VerifierSomeClient {
return &VerifierSomeClient{mock, invocationCountMatcher, nil}
}
func (mock *MockSomeClient) VerifyWasCalledInOrder(invocationCountMatcher pegomock.Matcher, inOrderContext *pegomock.InOrderContext) *VerifierSomeClient {
return &VerifierSomeClient{mock, invocationCountMatcher, inOrderContext}
}
type VerifierSomeClient struct {
mock *MockSomeClient
invocationCountMatcher pegomock.Matcher
inOrderContext *pegomock.InOrderContext
}
func (verifier *VerifierSomeClient) WithContext(_param0 context.Context) *SomeClient_WithContext_OngoingVerification {
params := []pegomock.Param{_param0}
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "WithContext", params)
return &SomeClient_WithContext_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
}
type SomeClient_WithContext_OngoingVerification struct {
mock *MockSomeClient
methodInvocations []pegomock.MethodInvocation
}
func (c *SomeClient_WithContext_OngoingVerification) GetCapturedArguments() context.Context {
_param0 := c.GetAllCapturedArguments()
return _param0[len(_param0)-1]
}
func (c *SomeClient_WithContext_OngoingVerification) GetAllCapturedArguments() (_param0 []context.Context) {
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
if len(params) > 0 {
_param0 = make([]context.Context, len(params[0]))
for u, param := range params[0] {
_param0[u] = param.(context.Context)
}
}
return
}
Interesting. I just tried to reproduce this and couldn't. I copied your code verbatim (btw thanks, this made it trivial to reproduce!) and got this:
$ go run main.go
Matcher Type: *context.valueCtx - Param Type: *context.valueCtx -> matches true
Shouldn't be nil &{<nil>}
To make that last line more readable, I changed it to use https://github.com/davecgh/go-spew:
func NewEndpoint(client SomeClient) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
clientWithContext := client.WithContext(r.Context())
spew.Dump(clientWithContext)
}
}
That gives me:
$ go run main.go
Matcher Type: *context.valueCtx - Param Type: *context.valueCtx -> matches true
(*main.MockSomeClient)(0xc42009c040)({
fail: (func(string, ...int)) <nil>
})
I'm using
$ go version
go version go1.7.3 linux/amd64
Is it possible that you're using a different go version?
Hi Peter
Thanks for answering so quickly.
I am puzzled. I ran the original tests on OS/X so, now I tried it on a linux VM but I am getting the same failure result.
-bash-4.2$ go run main.go
O: *context.valueCtx, P: *context.valueCtx - match false
(interface {}) <nil>
-bash-4.2$ go version
go version go1.7.3 linux/amd64
-bash-4.2$
I'm asking my coworkers to run it on their machines.
Okay so my coworkers ran it and get the same result as you. I set up go on my Windows machine and I also get the expected, correct result.
So something really odd seems to be going on with my Mac...anyway, closing.
Thanks for your help