godouble

package
v0.0.2 Latest Latest
Warning

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

Go to latest
Published: Feb 15, 2020 License: Apache-2.0 Imports: 9 Imported by: 221

Documentation

Overview

Package godouble is a TestDouble framework for Go.

This framework creates a TestDouble implementation of an interface which can be substituted for the real thing during tests. Interface methods can then individually be Stubbed, Mocked, Spied upon or Faked as required.

Stubs, Mocks, Spies, Fakes

See the canonical sources...

* http://xunitpatterns.com/Test%20Double.html

* https://martinfowler.com/articles/mocksArentStubs.html

A Stub provides specific return values for a matching call to the method. Most useful where the return values are the primary means by which correct operation of the system under test can be verified.

 package examples

 import (
	. "github.com/lwoggardner/godouble" //Note the dot import which assists with readability
	"testing"
 )

 func Test_Stub(t *testing.T) {
	d := NewAPIDouble(t) // A specific implementation of a TestDouble

	//Stub a method that receives specific arguments, to return specific values
	d.Stub("SomeQuery").Matching(Args(Eql("test"))).Returning(Values(Results{"result"}, nil))

	// Exercise the system under test substituting d for the real API client
	// ...

	// Verify assertions to confirm the system under test behaves as expected with the given return values
	// ...
 }

A Mock is a Stub with an up-front expectation for how many times it will be called. Most useful when the return values of the method do not completely ensure correct functioning of the system under test,

 func Test_Mock(t *testing.T) {
	d := NewAPIDouble(t)
    // Verify the mock expectations are met at completion
	defer d.Verify()

	//Stub a method that receives specific arguments, returns specific values and explicitly expects to be called once
	d.Mock("SomeQuery").Matching(Args(Eql("test"))).Returning(Values(Results{"result"}, nil)).Expect(Exactly(3))
	d.Mock("OtherMethod").Expect(Never())

    //Exercise...
 }

A Spy is a record of all calls made to a method which can be verified after exercising the system under test. Used similarly to Mock, but where you prefer to explicitly assert received arguments and call counts in the Verify phase of the test.

 func Test_Spy(t *testing.T) {
	//Setup
	d := NewAPIDouble(t)

	spy := d.Spy("SomeQuery").Returning(Values(Results{"nothing"}, nil))

	//Exercise...

	//Verify
    spy.Expect(Twice()) //All calls
	spy.Matching(Args(Eql("test"))).Expect(Once()) //The subset of calls with matching args

 }

A Fake is a Spy that provides an actual implementation of the method instead of return values. Use with caution.

 func Test_Fake(t *testing.T) {
	//Setup
	d := NewAPIDouble(t)
	impl := func( i int, options...string) *Results {
		return &Results{Output: fmt.Sprintf("%s %d",strings.Join(options," "),i)}
	}

	spy := d.Fake("QueryWithOptions",impl)

	//Exercise...

	//Verify
	spy.Expect(Twice())
	spy.Matching(Args(Eql(10))).Expect(Once())

 }

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func AssertMethodInputs

func AssertMethodInputs(t T, m reflect.Method, funcType reflect.Type)

AssertMethodInputs fatally fails test t unless funcType has compatible input methods with method m

func AssertMethodOutputs

func AssertMethodOutputs(t T, m reflect.Method, funcType reflect.Type)

AssertMethodOutputs Fatally fails test t unless funcType's return types are compatible with m's return types

func AssertMethodReturnTypes

func AssertMethodReturnTypes(t T, m reflect.Method, returnTypes []reflect.Type, prefixes ...interface{})

AssertMethodReturnTypes fatally fails test t unless returnTypes are compatible with method m's return types

func AssertMethodReturnValues

func AssertMethodReturnValues(t T, method reflect.Method, returnValues []interface{})

AssertMethodReturnValues fatally fails test t unless returnValues are compatible with method

func ExpectInOrder

func ExpectInOrder(calls ...MockedMethodCall)

ExpectInOrder is shorthand to Setup that the list of calls are expected to executed in this sequence

func Verify

func Verify(testDoubles ...Verifiable)

Verify is shorthand to Verify a set of TestDoubles

Types

type CombinationMatcher

type CombinationMatcher interface {
	Matcher
	ForMethod(t T, m reflect.Method)
	ForType(t T, ft reflect.Type)
}

A CombinationMatcher is a Matcher than can validated usage for both methods and types

func All

func All(matchers ...Matcher) CombinationMatcher

All matches if all the matcherList match (returns true for no matchers)

func And

func And(matchers ...Matcher) CombinationMatcher

And matches if all the matcherList match

func Any

func Any(matchers ...Matcher) CombinationMatcher

Any matches if any one of matcherList match (returns false for no matchers)

func Func

func Func(f interface{}, explanation ...interface{}) CombinationMatcher

Func returns a matcher from the arbitrary function f Custom matcher methods will generally be a wrapper around Func

When used as a method args matcher f(...) bool must have a compatible argument signature with the stubbed method When used as a single arg matcher f must be a func(x T) bool where T is assignable from the equivalent arg in the stubbed method Optionally include an explanation that will be formatted to string to describes what is being matched

func Not

func Not(matcher Matcher) CombinationMatcher

Not negates matcher

func Or

func Or(matchers ...Matcher) CombinationMatcher

Or matches if any one of matcherList match

type Completion

type Completion interface {
	Expectation
	Complete(count int) bool
}

A Completion is an expectation that can indicate that further calls will fail to meet the expectation Expectations that are not also Completions are never considered complete

func AtMost

func AtMost(n int) Completion

AtMost returns an expectation to be called at most n times This expectation is considered complete after being exercised n times

func Between

func Between(min int, max int) Completion

Between returns a new expectation that a method is exercised at least min times and at most max times The expectation is considered complete after being exercised max times

func Exactly

func Exactly(n int) Completion

Exactly returns an expectation to be called exactly n times This expectation is considered complete after being exercised n times

func Once

func Once() Completion

Once is shorthand for Exactly(1)

func Twice

func Twice() Completion

Twice is shorthand for Exactly(2)

type Expectation

type Expectation interface {
	// Is the expectation met, complete with count?
	Met(count int) bool
}

An Expectation verifies a count against an expected Value

func AtLeast

func AtLeast(n int) Expectation

AtLeast returns an expectation to be called at least n times

func Never

func Never() Expectation

Never returns an expectation to never be called

type FakeMethodCall

type FakeMethodCall interface {
	// The full set of all recorded calls to this method available to be verified
	RecordedCalls
	MethodCall
}

FakeMethodCall is a SpyMethodCall with a Fake implementation.

type Matcher

type Matcher interface {

	//Matches returns true if the arg (or args) matches this matcher
	Matches(args ...interface{}) bool
}

Matcher is used to match a method signature or one argument at a time

type MatcherForMethod

type MatcherForMethod func(t T, m reflect.Method, chained MethodArgsMatcher, matchers ...interface{}) MethodArgsMatcher

MatcherForMethod can be used to integrate a different matching framework

type Method

type Method interface {
	Stub() StubbedMethodCall
	Mock() MockedMethodCall
	Spy() SpyMethodCall
	Fake(impl interface{}) FakeMethodCall
	Reflect() reflect.Method
}

Method is used to configure the default Double type for a given interface method.

A method's signature is available via Reflect() Stub(), Mock(), Spy(), Fake() are used to return a specific MethodCall implementation to use See TestDoubleConfigurator

type MethodArgsMatcher

type MethodArgsMatcher interface {
	Matcher
	//ForMethod uses t to assert suitability of this matcher to match the method signature of m
	ForMethod(t T, m reflect.Method)
}

MethodArgsMatcher is a Matcher that can validate usage against a reflect.Method

func Args

func Args(matchers ...Matcher) MethodArgsMatcher

Args builds a method arguments matcher from a list of single ArgumentMatchers

func NewMatcherForMethod

func NewMatcherForMethod(t T, forMethod reflect.Method, matchers ...interface{}) (result MethodArgsMatcher)

type MethodCall

type MethodCall interface {
	// contains filtered or unexported methods
}

MethodCall is an abstract interface of specific call types, Stub, Mock, Spy and Fake

type MockedMethodCall

type MockedMethodCall interface {
	/*
			Matching is used to setup whether this call will match a given set of arguments.

		    Empty matcherList list will fatally fail the test

		    If the first matcher is a Matcher then it is used (test will fatally fail is more matcherList are sent)
		    If the first matcher is a func then is equivalent to Matching(Matcher(matcherList[0],matcherList[1:))
		    Otherwise each matcher is converted to a Matcher via either Func() or Eql()
		    and this list is sent to Args()
	*/
	Matching(matchers ...interface{}) MockedMethodCall

	//Setup that this call will only match if the supplied calls are already complete
	After(calls ...MockedMethodCall) MockedMethodCall

	/*
		Returning is used to setup return values for this call

		The returnValues are converted to a ReturnValues via Values()
	*/
	Returning(values ...interface{}) MockedMethodCall

	//Setup an expectation on the number of times this call will be invoked
	Expect(expect Expectation) MockedMethodCall

	MethodCall
	// contains filtered or unexported methods
}

MockedMethodCall is a MethodCall that has pre-defined expectations for how often and sequence of invocations

type RecordedCalls

type RecordedCalls interface {
	/*
		Matching returns the subset of calls that match

		Empty matcherList list will fatally fail the test

		If the first matcher is a Matcher then it is used (test will fatally fail is more matcherList are sent)
		If the first matcher is a func then is equivalent to Matching(Matcher(matcherList[0],matcherList[1:))
		Otherwise each matcher is converted to a Matcher via either Func() or Eql()
		and this list is sent to Args()
	*/
	Matching(matchers ...interface{}) RecordedCalls

	/*
		Slice returns a subset of these calls, including call at index from, excluding call at index to (like go slice))

		If necessary use NumCalls() to reference calls from the end of the slice.
		eg to get the last 3 calls - r.Slice(r.NumCalls() -3, r.NumCalls())
	*/
	Slice(from int, to int) RecordedCalls

	// After returns the subset of these calls that were invoked after all of otherCalls
	After(otherCalls RecordedCalls) RecordedCalls

	// Expect asserts the number of calls in this set
	Expect(expect Expectation)

	// NumCalls returns the number of calls in this set.
	// Prefer to use Expect() rather than asserting the result of NumCalls()
	NumCalls() int
	// contains filtered or unexported methods
}

RecordedCalls represents a set of recorded call invocations to be verified

type ReturnChannel

type ReturnChannel interface {

	//Send a list of return values
	Send(...interface{})

	//Close the channel, subsequent invocations that need values will cause the test to fail fatally
	Close()

	//Set a timeout. If the timeout expires before a Value is available on the channel
	//  ( via Send() ) the test will fail fatally.
	SetTimeout(timeout time.Duration, sleeper ...Timewarp)

	ReturnValues
}

ReturnChannel provides channel semantics for returning values from stub calls

func NewReturnChannel

func NewReturnChannel(bufferSize ...int) ReturnChannel

NewReturnChannel generates return values for successive calls to a stub. It will return errors if the channel is closed

Use the optional bufferSize parameter with a non-zero Value to create a buffered channel.

Use SetTimeout() to override the default timeout of 200 ms.

type ReturnValues

type ReturnValues interface {

	//Receive is called when a method is exercised
	//
	// non nil error response will fatally terminate the test
	Receive() ([]interface{}, error)
}

ReturnValues implementations generate values in response to Stub, Mock or Spy method invocations

func Delayed

func Delayed(rv ReturnValues, by time.Duration, sleep ...Timewarp) ReturnValues

Delayed wraps the ReturnValues rv with a fixed delay of 'by' duration

Useful to simulate an asynchronous IO request, allowing other goroutines to run while waiting for the response.

An optional sleeper function, defaulting to time.Sleep, can be provided. eg for use with fake clock

func NewReturnsForMethod

func NewReturnsForMethod(t T, forMethod reflect.Method, values ...interface{}) (rv ReturnValues)

func RandDelayed

func RandDelayed(rv ReturnValues, max time.Duration, sleep ...Timewarp) ReturnValues

RandDelayed wraps the ReturnValues rv with a delay of up to 'max' duration

func Sequence

func Sequence(values ...ReturnValues) ReturnValues

Sequence returns values from each of 'values' until there are no further values available

func Values

func Values(values ...interface{}) ReturnValues

Values stores a fixed set of values returned for every invocation

func ZeroValues

func ZeroValues(methodType reflect.Type) ReturnValues

ZeroValues repeatedly returns the zeroed values for the given methodType

type ReturnsForMethod

type ReturnsForMethod func(t T, m reflect.Method, chained ReturnValues, returnValues ...interface{}) ReturnValues

ReturnsForMethod can be used to integrate a different return values framework

type SingleArgMatcher

type SingleArgMatcher interface {
	Matcher

	//ForType uses t to assert suitability of this matcher to match a single argument of type ft
	ForType(t T, ft reflect.Type)
}

A SingleArgMatcher is a Matcher that can validate usage against a reflect.Type

func Eql

func Eql(v interface{}) SingleArgMatcher

Eql matches a single argument v via reflect.DeepEqual

func IsA

func IsA(t interface{}) SingleArgMatcher

IsA matches a single argument if the supplied argument is AssignableTo or Implements the reflect.Type t

if t is not already a reflect.Type it will be converted with reflect.TypeOf

func Len

func Len(v interface{}) SingleArgMatcher

Len matches a single argument that is type Array,Chan,Map,Slice,String type that have length matching v

l may be anything that can match an int eg

Len(0)
Len(func(l int) bool { l <= 10})

func Nil

func Nil() SingleArgMatcher

Nil matches a single argument of any nil-able type to be nil (or equivalent)

func Slice

func Slice(matchers ...Matcher) SingleArgMatcher

Slice returns a Matcher for a Slice type from a list of other SingleArgumentMatchers

If all the matcherList match the argument in the corresponding position of the newSliceMatcher

type SpyMethodCall

type SpyMethodCall interface {
	/*
		Returning is used to setup return values for this call

		The returnValues are converted to a ReturnValues via Values()
	*/
	Returning(values ...interface{}) SpyMethodCall

	// The full set of all recorded calls to this method available to be verified
	RecordedCalls

	MethodCall
}

SpyMethodCall is a MethodCall that records method invocations for later verification

type StubbedMethodCall

type StubbedMethodCall interface {
	/*
			Matching is used to setup whether this call will match a given set of arguments.

		    Empty matcherList list will fatally fail the test

		    If the first matcher is a Matcher then it is used (test will fatally fail is more matcherList are sent)
		    If the first matcher is a func then is equivalent to Matching(Matcher(matcherList[0],matcherList[1:))
		    Otherwise each matcher is converted to a Matcher via either Func() or Eql()
		    and this list is sent to Args()
	*/
	Matching(matchers ...interface{}) StubbedMethodCall

	/*
		Returning is used to setup return values for this call

		The returnValues are converted to a ReturnValues via Values()
	*/
	Returning(returnValues ...interface{}) StubbedMethodCall

	MethodCall
}

StubbedMethodCall is a MethodCall that matches a given set of arguments and returns pre-defined values.

type T

type T interface {
	Errorf(format string, args ...interface{})
	Fatalf(format string, args ...interface{})
	Logf(format string, args ...interface{})
	Helper()
}

T is compatible with builtin testing.T

type TestDouble

type TestDouble struct {
	// contains filtered or unexported fields
}

A TestDouble is an object that can substitute for a concrete implementation of an interface in a 4 phase testing framework (Setup, Exercise, Verify, Teardown).

Setup phase

Expected method calls to the double can be configured as one of the following types.

1) Stub - Returns known values in response to calls against matching input arguments

2) Mock - A stub with pre-built expectations about the number and order of method invocations on matching calls

3) Spy - A stub that records calls as they execute

4) Fake - A substitute implementation for the method

Exercise phase

Any methods invoked on the double are sent to the first matching call that has been configured. If no matching call is available, the DefaultMethodCallType for this double is generated.

Verify phase

The Verify() method is used to confirm expectations on Mock methods have been met.

Spies (and Fakes) have explicit methods to assert the number and order of method invocations on subsets of calls.

func NewDouble

func NewDouble(t T, forInterface interface{}, configurators ...func(*TestDouble)) *TestDouble

NewDouble Constructor for TestDouble called by specific implementation of test doubles.

forInterface is expected to be the nil implementation of an interface - (*Iface)(nil)

configurators are used to configure tracing and default behaviour for unregistered method calls and return values

func (*TestDouble) DisableTrace added in v0.0.2

func (d *TestDouble) DisableTrace()

func (*TestDouble) EnableTrace

func (d *TestDouble) EnableTrace()

Enable tracing of all received method calls (via T.Logf)

func (*TestDouble) Fake

func (d *TestDouble) Fake(methodName string, impl interface{}) (fake FakeMethodCall)

Fake installs a user implementation for the method.

Setup Phase

Install the Fake implementation, which must match the signature of the method.

Only one fake is installed for a method, and clobbers any other configured calls.

Exercise Phase

Invokes the fake function via reflection, and records the call as per Spy.

Verify Phase

Explicitly verify RecordedCalls as per Spy.

func (*TestDouble) Invoke

func (d *TestDouble) Invoke(methodName string, args ...interface{}) []interface{}

Invoke is called by specialised mock implementations, and sometimes by Fake implementations to record the invocation of a method.

func (*TestDouble) Mock

func (d *TestDouble) Mock(methodName string) (mock MockedMethodCall)

Mock adds and returns a MockedMethodCall for methodName on TestDouble d

Setup Phase

Configure Matcher, sequencing (After), and Return Values.

Set Expectation on number of matching invocations.

By default a MockedMethodCall matches any arguments, returns zero values for all outputs and expects exactly one invocation.

Exercise Phase

The first mock matching the invocation arguments and not yet Complete in terms of Expectation will provide the output values.

Verify Phase

(via call to a TestDouble.Verify() usually deferred immediately after the double is created)

Will assert the Expectation is met.

func (*TestDouble) SetDefaultCall

func (d *TestDouble) SetDefaultCall(defaultCall func(Method) MethodCall)

SetDefaultCall allows caller to provide a function to decide whether to Stub, Mock, Spy or Fake a call that was not explicitly registered in Setup phase.

the default function is a mock that never expects to be called.

func (*TestDouble) SetDefaultReturnValues

func (d *TestDouble) SetDefaultReturnValues(defaultReturns func(Method) ReturnValues)

SetDefaultReturnValues allows a caller to provide a function to generate default return values for a Stub, Mock, or Spy that was not explicitly registered with ReturnValues during Setup. The default is to used zeroed values via reflection.

func (*TestDouble) SetMatcherIntegration

func (d *TestDouble) SetMatcherIntegration(forMethod MatcherForMethod)

func (*TestDouble) SetReturnValuesIntegration

func (d *TestDouble) SetReturnValuesIntegration(forMethod ReturnsForMethod)

func (*TestDouble) Spy

func (d *TestDouble) Spy(methodName string) (spy SpyMethodCall)

Spy records all calls to methodName.

Setup Phase

Configure ReturnValues.

Calling Spy twice for the same method will return the same Value (ie there is only every one spy, and it will record methods that do not match any preceding Stub or Mock calls)

Exercise Phase

Matches and records all invocations.

Verify Phase

Can be called again to retrieve the spy for the method (eg to get a dynamically created default Spy).

Extract subsets of RecordedCalls and then verify an Expectations on the number of calls in the subset.

func (*TestDouble) String

func (d *TestDouble) String() string

func (*TestDouble) Stub

func (d *TestDouble) Stub(methodName string) (stub StubbedMethodCall)

Stub adds and returns a StubbedMethodCall for methodName on TestDouble d

Setup phase

Configure Matcher and ReturnValues.

By default a StubbedMethodCall matches any arguments and returns zero values for all outputs.

Exercise Phase

The first stub matching the invocation arguments will provide the output values.

Verify Phase

Nothing to verify

func (*TestDouble) T

func (d *TestDouble) T() T

func (*TestDouble) Verify

func (d *TestDouble) Verify()

type Timewarp added in v0.0.2

type Timewarp func(d time.Duration) <-chan time.Time

A Timewarp can be used to simulate a sleep, eg when testing using a fake clock. The canonical sleeper is

time.After

type ValidatingReturnValues

type ValidatingReturnValues interface {
	ReturnValues
	ForMethod(t T, method reflect.Method)
}

type Verifiable

type Verifiable interface {
	Verify()
}

Jump to

Keyboard shortcuts

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