README ¶
ifacegen
Ifacegen generates skeleton code of a type to satisfy an interface. It
can be used to generate snippet that satisfies an interface, such as
sort.Interface
, or generate mock struct in tests.
Ifacegen is intuitive to use. The interface is specified as
{package_import_path}.{interface_name}
,
e.g. google.golang.org/grpc.Compressor
. The package can be a
vendored one, in GOPATH
or GOROOT
. {package import path}.
is
optional. If no {package import path}.
is specified, the
{interface_name}
is looked up in the local package.
Different from other mock tools, such as gomock, ifacegen takes a minimalistic approach. Ifacegen does not introduce any DSL, and tries to generate code that is like manually written by developer.
Ifacegen generates the method stubs of a struct to satisfy the interface, but leave the actual code to mock the methods to developer. It is up to developer to decide how to verify the input and/or return the output.
Code Snippet
You can use ifacegen
to generate method stubs of an interface.
For example, to implement sort.Interface of a type fooSlice
, you run ifacegen
to generate the method stubs,
ifacegen -r fooSlice -i sort.Interface
This generates the following code snippet and print to stdout,
func (m fooSlice) Len() int {
}
func (m fooSlice) Less(i int, j int) bool {
}
func (m fooSlice) Swap(i int, j int) {
}
Now you can fill in the implementation details of these methods.
Mock
Ifacegen can generate mock struct to satisfy an interface as well.
For example, to mock net.Conn
, you run ifacegen
to generate a mock struct,
ifacegen -i net.Conn -m -o netconn_mock_test.go
This generates a file netconn_mock_test.go
with the code below,
// @generated by ifacegen
package main
import (
"net"
"sync/atomic"
"time"
)
const (
callClose = 0
callLocalAddr = 1
callRead = 2
callRemoteAddr = 3
callSetDeadline = 4
callSetReadDeadline = 5
callSetWriteDeadline = 6
callWrite = 7
)
type ConnMock struct {
PanicIfNotMocked bool
CloseMock func() error
LocalAddrMock func() net.Addr
ReadMock func(b []byte) (n int, err error)
RemoteAddrMock func() net.Addr
SetDeadlineMock func(t time.Time) error
SetReadDeadlineMock func(t time.Time) error
SetWriteDeadlineMock func(t time.Time) error
WriteMock func(b []byte) (n int, err error)
callCounts [8]int32
}
func (m *ConnMock) Close() (err error) {
atomic.AddInt32(&m.callCounts[callClose], 1)
if m.CloseMock == nil {
if m.PanicIfNotMocked {
panic("Close is not mocked")
}
return err
}
return m.CloseMock()
}
func (m *ConnMock) CloseCallCount() int {
return int(atomic.LoadInt32(&m.callCounts[callClose]))
}
func (m *ConnMock) LocalAddr() (r0 net.Addr) {
atomic.AddInt32(&m.callCounts[callLocalAddr], 1)
if m.LocalAddrMock == nil {
if m.PanicIfNotMocked {
panic("LocalAddr is not mocked")
}
return r0
}
return m.LocalAddrMock()
}
func (m *ConnMock) LocalAddrCallCount() int {
return int(atomic.LoadInt32(&m.callCounts[callLocalAddr]))
}
func (m *ConnMock) Read(b []byte) (n int, err error) {
atomic.AddInt32(&m.callCounts[callRead], 1)
if m.ReadMock == nil {
if m.PanicIfNotMocked {
panic("Read is not mocked")
}
return n, err
}
return m.ReadMock(b)
}
func (m *ConnMock) ReadCallCount() int {
return int(atomic.LoadInt32(&m.callCounts[callRead]))
}
func (m *ConnMock) RemoteAddr() (r0 net.Addr) {
atomic.AddInt32(&m.callCounts[callRemoteAddr], 1)
if m.RemoteAddrMock == nil {
if m.PanicIfNotMocked {
panic("RemoteAddr is not mocked")
}
return r0
}
return m.RemoteAddrMock()
}
func (m *ConnMock) RemoteAddrCallCount() int {
return int(atomic.LoadInt32(&m.callCounts[callRemoteAddr]))
}
func (m *ConnMock) SetDeadline(t time.Time) (err error) {
atomic.AddInt32(&m.callCounts[callSetDeadline], 1)
if m.SetDeadlineMock == nil {
if m.PanicIfNotMocked {
panic("SetDeadline is not mocked")
}
return err
}
return m.SetDeadlineMock(t)
}
func (m *ConnMock) SetDeadlineCallCount() int {
return int(atomic.LoadInt32(&m.callCounts[callSetDeadline]))
}
func (m *ConnMock) SetReadDeadline(t time.Time) (err error) {
atomic.AddInt32(&m.callCounts[callSetReadDeadline], 1)
if m.SetReadDeadlineMock == nil {
if m.PanicIfNotMocked {
panic("SetReadDeadline is not mocked")
}
return err
}
return m.SetReadDeadlineMock(t)
}
func (m *ConnMock) SetReadDeadlineCallCount() int {
return int(atomic.LoadInt32(&m.callCounts[callSetReadDeadline]))
}
func (m *ConnMock) SetWriteDeadline(t time.Time) (err error) {
atomic.AddInt32(&m.callCounts[callSetWriteDeadline], 1)
if m.SetWriteDeadlineMock == nil {
if m.PanicIfNotMocked {
panic("SetWriteDeadline is not mocked")
}
return err
}
return m.SetWriteDeadlineMock(t)
}
func (m *ConnMock) SetWriteDeadlineCallCount() int {
return int(atomic.LoadInt32(&m.callCounts[callSetWriteDeadline]))
}
func (m *ConnMock) Write(b []byte) (n int, err error) {
atomic.AddInt32(&m.callCounts[callWrite], 1)
if m.WriteMock == nil {
if m.PanicIfNotMocked {
panic("Write is not mocked")
}
return n, err
}
return m.WriteMock(b)
}
func (m *ConnMock) WriteCallCount() int {
return int(atomic.LoadInt32(&m.callCounts[callWrite]))
}
Now you can use ConnMock
and set the interesting *Mock
funcs in your tests. For example, to test Read
,
func TestFoo(t *testing.T) {
conn := &ConnMock{
ReadMock: func(b []byte) (int, error) {
return copy(b, "hello"), nil
},
}
...
got := make([]byte, 10)
n, err := conn.Read(got)
...
}
Usage
Usage of ifacegen:
-i string
Interface name, [{import_path}.]{Interface}, e.g. net/http.Handler, Foo. (Required)
-m Generate mock struct if true
-o string
Name of output file, default to os.Stdout
-r string
Name of receiver, default to *{Interface}{Gen|Mock}
-t Put the mock struct in test package if true
where,
{import_path}
is the package import path as specified in the import declaration, e.g.net/http
,golang.org/x/crypto/nacl/box
. The package can be a vendored one.{import_path}.
is optinal. When{import_path}
is not specified, the interface is searched in the local package.- if you want the receiver to be point receiver, prefix the name with
*
, e.g.-r '*MyHandler'
. The default receiver is point receiver. - if
-m
is true, the mock struct is generated; otherwise code snippet is generated. The mock struct is in the local package. But if-t
is true, the mock struct is in the{local_package}_test
package.
Documentation ¶
Overview ¶
Ifacegen generates skeleton code of a type to satisfy an interface. For example, to generate code to satisfy sort.Interface,
ifacegen -r stringSlice -i sort.Interface
Ifacegen can generate mock struct of an interface as well by specifying a package name. For example,
ifacegen -p myhttp -o httphandler_mock.go -i net/http.Handler