gomuti

package module
v0.0.0-...-4fd413f Latest Latest
Warning

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

Go to latest
Published: Apr 25, 2017 License: MIT Imports: 5 Imported by: 0

README

Build Status Go Report Docs Gomuti

Gomuti is DSL for mocking Golang interfaces, inspired by Gomega and drawing upon Gomega matchers to dispatch mock method calls. With a Ginkgo-like DSL for programming mock behavior, Gomuti makes it easy to write beautiful, well-isolated unit tests.

Mocks can also be spies and stubs, enabling behavior-driven development and terse, easy-to-maintain mock setup.

How to use

Imagine you have an interface that you want to mock.

type Adder interface {
  Add(l, r int64) int64
}

To properly mock this interface, you need to create a struct type that has the same methods. The struct also holds two Gomuti-related fields that keep state about the programmed behavior of the mock and the observed method calls:

  import gtypes "github.com/xeger/gomuti/types"

  type MockAdder struct {
    Mock gtypes.Mock
    Spy gtypes.Spy
  }

  func(m *MockAdder) Add(l, r int64) int64 {
    m.Spy.Observe(l, r)
    r := m.Mock.Call("Add", l, r)
    return r[0].(int64)
  }

In reality you would use Mongoose to generate a mock type and methods for every interface in your package, but a hand-coded mock is fine for example purposes.

To program behavior into your mock, use the DSL methods in the gomuti package. Allow() instructs your mock to expect a method call and tells it what to return.

Mocking calls: Allow()

Imagine you're writing unit tests for the Multiplier type and you want to isolate yourself from bugs in Adder.

  import (
    . "github.com/onsi/ginkgo"
    . "github.com/xeger/gomuti"
  )

  Describe("multiplier", func() {
    var subject *multiplier
    var adder Adder
    BeforeEach(func() {
      adder = &MockAdder{}
      m := &multiplier{Adder:a}
    })

    It("computes the product of two integers", func() {
      Allow(adder).Call("Add").With(5,5).Return(10)
      Allow(adder).Call("Add").With(10,5).Return(15)
      result := subject.Multiply(3,5))
      Expect(result).To(Equal(15))
    })
  })

The Allow() DSL can use any Gomega matcher for method parameters and Gomuti provides a few matchers of its own; together, these allow you to mock sophisticated behavior. Imagine your adder has a new AddStuff() feature that adds arbitrarily-typed values.

  Allow(adder).Call("AddStuff").With(AnythingOfType("bool"), Anything()).Return(true)

Spying on mocks: HaveCall()

You can use the HaveCall() Gomega matcher to spy on your mock, verifying the number of calls actually made to your mock as well as the specific parameter values.

integer := AnythingOfType("int64")
Expect(adder).To(HaveCall("Add").With(integer, integer).Times(2))

Stubbing calls

If you generate your mocks with Mongoose, then they come with a boolean Stub field; setting this field to true causes all methods to return zero values unless a matching call has been programmed.

RSpec DSL

Gomuti has some method aliases that imitate RSpec's plain-English DSL.

Expect(adder).ToReceive("Add").With(42,Anything()).AndReturn(42)
adder.Add(42, 7)
Expect(adder).To(HaveReceived("Add").Once())

Terse DSL

Gomuti's long-form DSL uses concise English words as method names. There is also a short-form DSL built around the method gomuti.Â(). To produce the  character, type Alt+0194 on Windows keyboards or Shift+Option+M on Mac keyboards (as a mnemonic, think " allows my Mock the option of being called.")

Short-form equivalents are provided for ToReceive() and other chained methods, and a super-terse form of  delivers maximum brevity. If we also use Gomega's Ω method, our tests get very terse indeed. (Some would say "unreadable," but beauty is in the eye of the beholder.)

  // Super terse DSL
  Â(adder,"Add",5,5).Return(10)

  // Moderately terse DSL with complex matcher.
  big := BeNumerically(">",2**32-1)
  Â(adder).Call("Add").With(big,Anything()).Panic("integer overflow")

  Ω(subject.Multiply(2,5)).Should(Equal(10))
  Ω(adder).Should(HaveCall("Add").Times(2))

  Ω(func() {
    subject.Multiply(2**32-1,1)
  }).Should(Panic())

Long and short method calls are interchangeable; even when using the long-form Allow(), we recommended using Call() instead of ToReceive() because the word "receive" is usually associated with the channel-receive operation.

How to get help

Check the frequently-asked questions to see if your problem is common.

Make sure to check Gomuti's godocs for relevant information.

If you think Gomuti is missing a feature, check the roadmap to see if a similar feature is already planned.

If you still need help, open an Issue. Clearly explain your problem, steps to reproduce, and your ideal solution (if known).

How to contribute

Fork the xeger/gomuti repository on GitHub; make your changes; open a pull request.

Documentation

Overview

Package gomuti provides a DSL that makes it easy to create test doubles (mocks, spies and stubs) for Golang interfaces. The DSL consists of three components:

1) Allow: a mocking method that records behavior for test doubles.

2) HaveCall: a spying method that verifies test doubles were called in an expected way.

3) Anything, AnythingOfType: parameter matchers used with mocking and spying methods. Gomega matchers can be used as Gomuti parameter matchers: BeNumerically, HaveOccurred, etc.

All of these methods rely on the Mock and Spy types exported by package gomuti/types; test doubles are generally struct types that contain exported fields of type Mock and Spy. The DSL operates on pointers to these structs and uses reflection to access their fields.

The DSL methods accept struct values as well as pointer-to-struct; the benefit of passing pointers is that the nested Mock or Spy will be allocated as needed with no intervention by the caller.

Stubbing is provided by the mongoose package (https://github.com/xeger/mongoose), which also generates Gomuti-compatible mock code for any interface. Stubbed methods are called whenever no mock expectations match a method call; the return value(s) from a stubbed method call are always zero values. Stubbing must be enabled on a per-object basis by setting the Stub field to true.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Allow

func Allow(double interface{}) *types.Allowed

Allow is a mocking method. It accepts a test double and returns a DSL-context object whose methods allow you to specify the test double's behavior when its methods are called.

For information about parameter matching and return values, see types.Allowed.

func Anything

func Anything() types.GomegaMatcher

Anything is an alias for BeAnything, designed to be more readable in the context of a mocked method call.

Example:

Allow(boat).Call("Sail").With("west", Anything(), "mi").Panic("Please use kilometers")

Which makes more sense than "with be-anything."

func AnythingOfType

func AnythingOfType(name string) types.GomegaMatcher

AnythingOfType is an alias for HaveType, designed to be more readable in the context of a mocked method call.

Example:

Allow(myMock).ToReceive("Foo").With(AnythingOfType("mypkg.Widget"))

Which makes more sense than "with have-type."

func BeAnything

func BeAnything() types.GomegaMatcher

BeAnything creates a matcher that is always satisfied. It is useful when you want to mock a method call but don't care about the parameter in a given position.

Example:

Allow(boat).Call("Sail").With("west", BeAnything(), "mi").Panic("Please use kilometers")

func HaveCall

func HaveCall(method string) *matchers.HaveCallMatcher

HaveCall is a spy method. It returns a matcher to verify that a method call was recorded by a spy. You can add more verifications (of parameter values, call count, etc) by calling methods on the returned matcher.

Example:

Expect(double).To(HaveCall("Bar").With(true, 42).Twice())

func HaveReceived

func HaveReceived(method string) *matchers.HaveCallMatcher

HaveReceived is an alias for HaveCall().

func HaveType

func HaveType(name string) types.GomegaMatcher

HaveType creates a matcher that is satisfied by any value whose type matches the specified name. Example:

Expect(4).To(HaveType("int"))

Named types must be prefixed with the name of the package in which they are DEFINED (i.e. with the name that appears in the package statement of the source file where they are defined) and not with the import name that is used to REFER to them. Example:

    import banana "time"
		 Expect(banana.Now()).To(HaveType("time.Time"))

func Â

func Â(double interface{}, methodAndParams ...interface{}) *types.Allowed

 is a mocking method that is an alias for Allow. Use Shift+Option+M to type this symbol on Mac; Alt+0194 on Windows.

As an additional shortcut, Â accepts the mocked method name and parameters as variadic parameters. The caller is still responsible for completing the behavior by calling Return or Panic() on the returned object.

Examples:

Â(double).Call("Foo").With(1,1).Return(2)  // no shortcuts
Â(double, "Foo").With(2,1).Return(3)       // shortcut call
Â(double, "Foo",3,1).Return(4)             // shortcut params

Types

This section is empty.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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