ifacegen

command module
v0.0.0-...-385a1c1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 27, 2019 License: GPL-3.0 Imports: 15 Imported by: 0

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

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL