Why another mocking library for GO
When I started writing mock4go, there were two other projects here and here that took a similar approach. The approach is to manually generate source code for interfaces using a provided tool.
I didn't like this approach for the following reasons:
- It requires a manual step of generating source code for each interfaces that will be used in testing.
- It only mock interfaces.
I decided to take a different approach after using gocov and being inspired by their approach. mock4go will create an instrumented copy of the code and run go test using the new copy. This allows mock4go to insert code to intercept function calls and do some interesting stuff.
Disclaimer
This library is still a work in progress and has some rough edges. I encourage you to start using it and contribute back or share your experience in order to improve it.
Install
go get github.com/jvshahid/mock4go/mock4go
Running the tests
./bin/mock4go my_package
For further help run ./bin/mock4go
.
Usage Example
The examples below use gocheck as the test framework. To stub a
function you call When(FunctionName([matchers])[.Return(returnValues)]
where:
matchers
are the argument matchers, currently we only support values matchers. A matcher equals the corresponding positional argument iff the matcher value wasn't a pointer and the argument equals the matcher value using reflect.DeepEquals, or the matcher value was a pointer and it is equal to the argument using==
returnValues
can be any number of return values (or even omitted)When
can be omitted if there's no aReturn
clause.- Currently mock4go doesn't check that the returnValues makes sense. That means that your test may compile but panics during runtime.
- Make sure you run ResetMocks() after each test
Stubbing functions wo a receiver
Given the very simple function below:
func OneReturnValueNoReceiver() string {
return "foo"
}
We can stub in the test like this:
package apackage
import (
. "github.com/jvshahid/mock4go"
. "launchpad.net/gocheck"
"testing"
)
type Mock4goSuite struct{}
var _ = Suite(&Mock4goSuite{})
func (suite *Mock4goSuite) TearDownTest(c *C) {
ResetMocks()
}
func (suite *Mock4goSuite) TestMockingFunctionsWithOneReturnValueAndNoReceiver(c *C) {
Mock(func() {
When(OneReturnValueNoReceiver()).Return("bar")
})
c.Assert(OneReturnValueNoReceiver(), Equals, "bar")
c.Assert(OneReturnValueNoReceiver2(), Equals, "foo2")
}
Stubbing functions with a receiver
type Foo struct {
Field string
}
func (f *Foo) NoReturnValues(value string) {
f.Field = value
}
func (suite *Mock4goSuite) TestMockingFunctionWithNoReturnValues(c *C) {
foo := &Foo{Field: ""}
bar := &Foo{Field: ""}
Mock(func() {
bar.NoReturnValues("bar") // cause the function to be a no operation
})
foo.NoReturnValues("foo")
bar.NoReturnValues("bar")
c.Assert(foo.Field, Equals, "foo")
// bar.NoReturnValues was stubbed and shouldn't change the value of Field
c.Assert(bar.Field, Equals, "")
}
Using matchers
mock4go allows you to write your own argument matchers.
func MultipleReturnValuesNoReceiver(value string) (string, error) {
return value, nil
}
type PrefixMatcher struct {
value string
}
func (m *PrefixMatcher) Matches(other interface{}) bool {
return strings.HasPrefix(other.(string), m.value)
}
func (suite *Mock4goSuite) TestMockingWithMatchers(c *C) {
expectedErr := errors.New("foobar")
Mock(func() {
When(MultipleReturnValuesNoReceiver("")).
WithMatchers(&PrefixMatcher{value: "ba"}). // ignore the values passed before and use the matcher instead
Return("foobar", expectedErr)
})
val, err := MultipleReturnValuesNoReceiver("foo")
c.Assert(err, IsNil)
c.Assert(val, Equals, "foo")
val, err = MultipleReturnValuesNoReceiver("bar")
c.Assert(err, Equals, expectedErr)
c.Assert(val, Equals, "foobar")
val, err = MultipleReturnValuesNoReceiver("baz")
c.Assert(err, Equals, expectedErr)
c.Assert(val, Equals, "foobar")
}
The reason you have to call the function with a dummy value is that there is no way to reliably compare function pointers in Go. A preferred way to do this is the following:
When(FunctionName, NewPrefixMatcher("ba")).Return("something")
but this will require the ability to test for "function" equality in mock4go which can't be reliably done. See http://golang.org/doc/go1.html#equality for more information about why it was decided to remove function equality in Go 1.0.
Stubbing interfaces
mock4go will create a mock implementation for every interface it parses.
If the interface is called FooInterface
the generated type will be called
MockFooInterface
, and all the interface's functions will be defined for
that type.
TODO
- Enhance the documentation of both the code and usage of the library
- Add more matchers, so we can do interesting things like match on a prefix, etc.
- Add a way to pass a new function that decides what to return to the caller
- Ability to exclude certain packages from being instrumented
Contributing
If you found a bug, want to add a feature:
- make sure you can run
./bin/test.sh
and it passes on your local machine - write a test in
src/test
and make sure it fails - make the test pass
- send me a pr
If you're feeling lazy or don't know how to fix a bug or implement a feature feel free to open a new issue and I'll make it happen.
License:
(The MIT License)
Copyright (c) 2013 :
* {John Shahid}[http://github.com/jvshahid]
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.