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:
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.
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.
go get github.com/jvshahid/mock4go/mock4go
./bin/mock4go my_package
For further help run ./bin/mock4go
.
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 a Return
clause.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")
}
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, "")
}
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.
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.
If you found a bug, want to add a feature:
./bin/test.sh
and it passes on your local machinesrc/test
and make sure it failsIf 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.
(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.