On 14 May 2016 at 17:56, Ben Sandler <
ben1s...@gmail.com> wrote:
> I'm trying to mock functions in the gorilla/websocket library for testing.
> First, I am trying to mock a call to `Dialer.Dial`.
Firstly, I'd suggest that it's often not necessary to mock the websocket
functions - you can quickly start (using http/httptest) an actual server,
and then use the real websocket functionality.
This can often expose bugs that would otherwise remain hidden
by the assumptions that you've made when interposing your
mocking layer.
However, assuming you *do* want to do this (and there are definitely
situations where it's warranted)...
> I did this by defining a
> wsDialer interface which has the type
>
> type wsDialer interface {
> Dial(string, http.Header) (wsConn, *http.Response, error)
> }
>
> It needs to return a connection which I also need to mock, so I created this
> other interface
>
> type wsConn interface {
>
> Close() error
> WriteJSON(interface{}) error
> WriteMessage(int, []byte) error
> ReadJSON(interface{}) error
> }
>
> I used mockery to create mock structs based on these interfaces.
>
> However, since `wsDialer` returns a `wsConn` and not a `*websocket.Conn`,
> the compiler does not think `websocket.Dialer` is a `wsDialer`. I really
> don't know how to get around this issue. Obviously, this would be
> significantly easier if gorilla/websocket used interfaces. Anyways, I have
> two ideas and am wondering if either will work.
There are two approaches that I use: a simple
but less general approach and a more general one.
In your case, since Dialer is a concrete type with all public methods, the
simpler one will work.
In your production code, define a variable like this. I try to keep
the name as close as possible to the original - hence the somewhat
long name. Note that this is a useful technique even if you're not
mocking interfaces - in that case you could avoid the func literal
and just use a method expression.
// websocketDialerDial is defined as a variable so it can be
// overridden for testing purposes.
var websocketDialerDial = func(d *websocket.Dialer, urlStr string,
requestHeader http.Header) (wsConn, *http.Response, error) {
return d.Dial(urlStr, requestHeader)
}
Then find all the places in your code that you currently invoke the Dialer.Dial
method, for example:
conn, resp, err := d.Dial(urlStr, h)
and change to:
conn, resp, err := websocketDialerDial(d, urlStr, h)
If there are lots of calls like this, consider using gofmt to make
the change automatically:
gofmt -w -r 'a.Dial(b, c) -> websocketDialerDial(a, b, c)'
Then if you use external tests, add in export_test.go:
var WebsocketDialerDial = &websocketDialerDial
Then in your testing code, you can mutate websocketDialerDial
for the duration of the test (I tend to use juju's testing.PatchValue):
r := testing.PatchValue(myPackage.websocketDialerDial, myMockFunc)
defer r.Restore()
If you had a multi-level interface, you'd need more than the simple
shim function defined as websocketDialerDial above.
You can define an interface that mirrors the to-be-mocked concrete
type except for having interface types anywhere you want to
mock a type, and omitting all the methods you don't care about.
Write a trivial implementation of this. Usually this is just a matter
of defining a concrete type that embeds the original concrete
type but overrides all the methods with now-incompatible signatures.
Using your original example, you could do:
type wsShim struct {
*websocket.Dialer
}
func (s wsShim) Dial(urlStr string, requestHeader http.Header)
(wsConn, *http.Response, error) {
// Note that because websocket.Dialer.Dial returns a concrete type here,
// the returned wsConn will never be nil even when Dial returns an
// error. If you care about this, test for error and
explicitly return nil.
return s.Dialer.Dial(urlStr, requestHeader)
}
If wsConn itself defined methods that you wanted to mock the return types of,
then this technique can be reused one level down:
func (s wsShim) Dial(urlStr string, requestHeader http.Header)
(wsConn, *http.Response, error) {
// Note that because websocket.Dialer.Dial returns a concrete type here,
// the returned wsConn will never be nil even when Dial returns an
// error. If you care about this, test for error and
explicitly return nil.
conn, resp, err := s.Dialer.Dial(urlStr, requestHeader)
if err != nil {
return nil, nil, err
}
return wsConnShim{conn}, resp, nil
}
type wsConnShim struct {
*websocket.Conn
}
func (wsConnShim) MethodReturningSomethingToBeMocked...
You can use this technique to mock arbitrarily many levels deep
(for example if you wanted to mock
gopkg.in/mgo.v2 's Database type)/
Note that because we're embedding a single pointer value inside
a concrete type, although there's extra production code here,
there is very little overhead because the interface values can
be created without allocation.
One more thing: in my view, packages like mockery add more cognitive
overhead than the effort they remove. I've found it to be quite quick and
effective to define ad hoc mock implementations by hand - the test code is more
statically typed and it's easier for third parties to see what's actually going
on in the tests.
Also, once again IMHO: if you can avoid mocking without undue overhead,
do so - it's very easy to create "smoke and mirrors" tests which look like
they're testing something but which are worthless in fact.
Hope this helps,
cheers,
rog.