ut

package module
v0.0.4 Latest Latest
Warning

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

Go to latest
Published: Dec 13, 2022 License: MIT Imports: 6 Imported by: 0

README

UT

UT allows you to automatically generate mock implementations of go interfaces for easy and awesome unit testing.

GoDoc Build Status

What's included

UT includes the following.

  • Code to help you build mock implementations of interfaces. You can say what calls you expect on the mock objects, what the parameters should be and what each call should return.
  • A tool (genmock) to automatically generate mocks from interface definitions.

The basic code is simple to understand and uses no magic. The auto code generation is not so simple to understand, but hopefully should work without you needing to look at it! Its perfectly reasonable to build a mock manually, and if you build tests as you build code it should not be a burden to do so.

genmock

genmock's parameters are as follows

  • package: name of the package or file containing the interface definition. Must be specified.
  • interface: name of the interface to create a mock for. Must be specified.
  • mock: name of the mock object to create. Defaults to Mock.
  • outfile: name of the file hold the mock definition. Defaults to mock.go in the current directory.
  • mock-package: name of the package to use in the mock definition. Must be specified.

Install genmock with go install github.com/philpearl/ut/genmock

You can then use it with go generate as follows. Add a go:generate comment as shown below (with no spaces within //go:generate), then run go generate to generate the files.

package mypackage

type MyInterface {
	func MakeACall(param string) error
}

// Generate a mock for MyInterface
//go:generate genmock -package=thisfile.go -interface=MyInterface -mock-package=mypackage

// Generate a mock for io.Reader
//go:generate genmock -package=io -interface=Reader -mock-package=mypackage

Example

This example is implemented as a test in this package. It creates a mock io.Reader, and tests the function UnderTest(). In this case I've built the mock by hand so you can see what kind of code genmock will generate.

Please also take a look at the example in https://github.com/philpearl/ut/example

You could also generate this mock with genmock -package=io -interface=Reader -mock-package=ut

package ut

import (
	"io"
	"testing"
)

// For this test we implement a mock of the io.Reader interface
type MockReader struct {
	CallTracker
}

// NewMockReader is a convenience method for creating our mock
func NewMockReader(t *testing.T) *MockReader {
	return &MockReader{NewCallRecords(t)}
}

// Here we implement the Read method of our mock io.Reader. This
// records the parameters passed to the call and returns values
// specified by the test
func (m *MockReader) Read(p []byte) (n int, err error) {
	r := m.TrackCall("Read", p)
	var r_0 int
	if r[0] != nil {
	    r_0 = r[0].(int)
	}
	var r_1 error
	if r[1] != nil {
		r_1 = r[1].(error)
	}
	return r_0, r_1
}

// This is the function we're going to test.
func UnderTest(r io.Reader) bool {
	p := make([]byte, 10)
	n, _ := r.Read(p)

	return n >= 1 && p[0] == 37
}

func TestUnderTest(t *testing.T) {

	// Define the tests we're going to run.
	tests := []struct {
		bytezero byte
		n        int
		expRet   bool
	}{
		{bytezero: 37, n: 1, expRet: true},
		{bytezero: 37, n: 2, expRet: true},
		{bytezero: 38, n: 2, expRet: false},
		{bytezero: 0, n: 2, expRet: false},
		{bytezero: 37, n: 0, expRet: false},
		{bytezero: 0, n: 0, expRet: false},
	}

	for _, test := range tests {
		// Set up the mock
		m := NewMockReader(t)

		// Parameters for AddCall can either be: values, which are compared against the actual parameter;
		// or functions, which can check and act on the parameter as they like
		checkReadParam := func(p any) {
			buf := p.([]byte)
			if len(buf) != 10 {
				t.Fatalf("should have read 10 bytes")
			}
			buf[0] = test.bytezero
		}

		// Note the calls we expect to happen when we run our test
		m.AddCall("Read", checkReadParam).SetReturns(test.n, error(nil))

		// Test the function
		if UnderTest(m) != test.expRet {
			t.Fatalf("return not as expected")
		}

		// Check the method calls we expected actually happened
		m.AssertDone()
	}
}

Documentation

Overview

Package ut implements some testing utilities. So far it includes CallTracker, which helps you build mock implementations of interfaces.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func NilOrError

func NilOrError(val any) error

NilOrError is a utility function for returning err from mocked methods

Types

type CallTracker

type CallTracker interface {
	// AddCall() is used by tests to add an expected call to the tracker
	AddCall(name string, params ...any) CallTracker

	// SetReturns() is called immediately after AddCall() to set the return
	// values for the call.
	SetReturns(returns ...any) CallTracker

	// TrackCall() is called within mocks to track a call to the Mock. It
	// returns the return values registered via SetReturns()
	TrackCall(name string, params ...any) []any

	// AssertDone() should be called at the end of a test to confirm all
	// the expected calls have been made
	AssertDone()

	// RecordCall() is called to indicate calls to the named mock method should
	// be recorded rather than asserted.  The parameters to any call to the
	// named method will be recorded and may be retrieved via GetRecordedParams.
	// The returns from the method are also specified on this call and must be
	// the same each time.
	// Note that the ordering of recorded calls relative to other calls is not
	// tracked.
	RecordCall(name string, returns ...any) CallTracker

	// GetRecordedParams returns the sets of parameters passed to a call captured
	// via RecordCall
	GetRecordedParams(name string) ([][]any, bool)
}

CallTracker is an interface to help build mocks.

Build the CallTracker interface into your mocks. Use TrackCall within mock methods to track calls to the method and the parameters used. Within tests use AddCall to add expected method calls, and SetReturns to indicate what the calls will return.

The tests for this package contain a full example.

 type MyMock struct {ut.CallTracker}

 func (m *MyMock) AFunction(p int) error {
	r := m.TrackCall("AFunction", p)
    return NilOrError(r[0])
 }

 func Something(m Functioner) {
    m.AFunction(37)
 }

 func TestSomething(t *testing.T) {
	m := &MyMock{NewCallRecords(t)}
    m.AddCall("AFunction", 37).SetReturns(nil)

    Something(m)

    m.AssertDone()
 }

func NewCallRecords

func NewCallRecords(t testing.TB) CallTracker

NewCallRecords creates a new call tracker

Directories

Path Synopsis
Code generated by genmock DO NOT EDIT.
Code generated by genmock DO NOT EDIT.

Jump to

Keyboard shortcuts

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