kelpie

package module
v0.0.0-...-bb57a55 Latest Latest
Warning

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

Go to latest
Published: Jun 2, 2024 License: MIT Imports: 2 Imported by: 0

README

Kelpie

What is Kelpie

Kelpie is the most magical mock generator for Go. Kelpie aims to be easy to use, and generates fully type-safe mocks for Go interfaces.

Project Status

At the moment Kelpie is very much in development, and there are missing features and some pretty rough edges. You're of course welcome to use Kelpie, but just be prepared to hit problems and raise issues or PRs!

The following is a list of known-outstanding features:

  • Generating mocks for inline interfaces in structs.

Quickstart

Install Kelpie:

go install github.com/adamconnelly/kelpie/cmd/kelpie@latest

Add a go:generate marker to the interface you want to mock:

//go:generate kelpie generate --package github.com/someorg/some/package --interfaces EmailService
type EmailService interface {
	Send(sender, recipient, body string) (cost float64, err error)
}

Use the mock:

emailServiceMock := emailservice.NewMock()
emailServiceMock.Setup(
	emailservice.Send("sender@sender.com", "someone@receiver.com", kelpie.Any[string]()).
	Return(100.54, nil)
)
emailServiceMock.Setup(
	emailservice.Send("sender@sender.com", "someone@forbidden.com", kelpie.Any[string]()).
	Return(0, errors.New("that domain is forbidden!"))
)

service := emailServiceMock.Instance()

service.Send("sender@sender.com", "someone@receiver.com", "Amazing message")
// Returns 100.54, nil

service.Send("sender@sender.com", "someone@forbidden.com", "Hello")
// Returns 0, errors.New("that domain is forbidden!)

Using Kelpie

Default Behaviour

No setup, no big deal. Kelpie returns the default values for method calls instead of panicking:

mock := emailservice.NewMock()
mock.Instance().Send("sender@sender.com", "someone@receiver.com", "Hello world")
// Returns 0, nil
Overriding an Expectation

Kelpie always uses the most recent expectation when trying to match a method call. That way you can easily override behaviour. This is really useful if you want to for example specify a default behaviour, and then later test an error condition:

mock := emailservice.NewMock()

// Setup an initial behaviour
mock.Setup(
	emailservice.Send(kelpie.Any[string](), kelpie.Any[string](), kelpie.Any[string]()).
	Return(200, nil)
)

service := mock.Instance()

cost, err := service.Send("sender@sender.com", "someone@receiver.com", "Hello world")
t.Equal(200, cost)
t.NoError(err)

// We override the mock, to allow us to test an error condition
mock.Setup(
	emailservice.Send(kelpie.Any[string](), kelpie.Any[string](), kelpie.Any[string]()).
	Return(0, errors.New("no way!"))
)

cost, err := service.Send("sender@sender.com", "someone@receiver.com", "Hello world")
t.Equal(0, cost)
t.ErrorEqual(err, "no way!")
Argument Matching
Exact Matching

By default Kelpie uses exact matching, and any parameters in a method call need to exactly match those specified in the setup:

emailServiceMock.Setup(
	emailservice.Send("sender@sender.com", "someone@receiver.com", "Hello world").
	Return(100.54, nil)
)
Any

You can match against any possible values of a particular parameter using kelpie.Any[T]():

emailServiceMock.Setup(
	emailservice.Send(kelpie.Any[string](), "someone@receiver.com", "Hello world").
	Return(100.54, nil)
)
Custom Matching

You can add custom argument matching functionality using kelpie.Match[T](isMatch):

emailServiceMock.Setup(
	emailservice.Send(
		kelpie.Match(func(sender string) bool {
			return strings.HasSuffix(sender, "@discounted-sender.com")
		}),
		"someone@receiver.com",
		"Hello world!").
		Return(50, nil))
Setting Behaviour
Returning a Value

To return a specific value from a method call, use Return():

emailServiceMock.Setup(
	emailservice.Send("sender@sender.com", "someone@receiver.com", "Hello world").
	Return(100.54, nil)
)
Panic

To panic, use Panic():

emailServiceMock.Setup(
	emailservice.Send("panic@thedisco.com", kelpie.Any[string](), "testing").
	Panic("Something has gone badly wrong!")
)
Custom Action

To perform a custom action, use When():

emailServiceMock.Setup(
	emailservice.Send(kelpie.Any[string](), kelpie.Any[string](), kelpie.Any[string]()).
		When(func(sender, recipient, body string) (float64, error) {
			// Do something
			return 0, nil
		}))
Verifying Method Calls

You can verify that a method has been called using the mock.Called() method:

// Arrange
mock := registrationservice.NewMock()

// Act
mock.Instance().Register("Mark")
mock.Instance().Register("Jim")

// Assert
t.True(mock.Called(registrationservice.Register("Mark")))
t.True(mock.Called(registrationservice.Register(kelpie.Any[string]()).Times(2)))
t.False(mock.Called(registrationservice.Register("Wendy")))
Times

You can configure a method call to only match a certain number of times, or verify a method has been called a certain number of times using the Times(), Once() and Never() helpers:

// Arrange
mock := registrationservice.NewMock()

// Act
mock.Instance().Register("Mark")
mock.Instance().Register("Jim")

// Assert
t.True(mock.Called(registrationservice.Register("Mark").Once()))
t.True(mock.Called(registrationservice.Register(kelpie.Any[string]()).Times(2)))
t.True(mock.Called(registrationservice.Register("Wendy").Never()))
Mocking an interface from an external package

Kelpie can happily mock interfaces that aren't part of your own source. You don't need to do anything special to mock an "external" interface - just specify the package and interface name you want to mock:

//go:generate go run ../cmd/kelpie generate --package io --interfaces Reader

func (t *ExternalTypesTests) Test_CanMockAnExternalType() {
	// Arrange
	var bytesRead []byte
	mock := reader.NewMock()
	mock.Setup(reader.Read(kelpie.Match(func(b []byte) bool {
		bytesRead = b
		return true
	})).Return(20, nil))

	// Act
	read, err := mock.Instance().Read([]byte("Hello World!"))

	// Assert
	t.NoError(err)
	t.Equal(20, read)
	t.Equal([]byte("Hello World!"), bytesRead)
}

FAQ

What makes Kelpie so magical

Kelpies are magical creatures from Scottish folk-lore that have shape-shifting abilities. This name seemed fitting for a mocking library, where generated mocks match the shape of interfaces that you want to simulate.

But other than that, there's nothing very magical about Kelpie.

Documentation

Overview

Package kelpie contains helpers for matching arguments when configuring a mock.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Any

func Any[T any]() mocking.Matcher[T]

Any matches any value of T.

func ExactMatch

func ExactMatch[T any](exactMatch T) mocking.Matcher[T]

ExactMatch matches parameters to the value of exactMatch.

func Match

func Match[T any](isMatch func(arg T) bool) mocking.Matcher[T]

Match uses isMatch to determine whether an argument matches.

Types

This section is empty.

Directories

Path Synopsis
cmd
kelpie
Package main contains the Kelpie code generator.
Package main contains the Kelpie code generator.
examples
mocks/accountservice
Code generated by Kelpie.
Code generated by Kelpie.
mocks/alarmservice
Code generated by Kelpie.
Code generated by Kelpie.
mocks/maths
Code generated by Kelpie.
Code generated by Kelpie.
mocks/reader
Code generated by Kelpie.
Code generated by Kelpie.
mocks/registrationservice
Code generated by Kelpie.
Code generated by Kelpie.
mocks/requester
Code generated by Kelpie.
Code generated by Kelpie.
mocks/sender
Code generated by Kelpie.
Code generated by Kelpie.
users
Package users contains utilities for working with users.
Package users contains utilities for working with users.
users/mocks/userrepository
Code generated by Kelpie.
Code generated by Kelpie.
Package maps contains utilities for working with maps.
Package maps contains utilities for working with maps.
Package mocking contains the mocking logic for Kelpie.
Package mocking contains the mocking logic for Kelpie.
Package nullable contains helpers for helping wrap and unwrap types with pointers.
Package nullable contains helpers for helping wrap and unwrap types with pointers.
Package parser contains the parser for reading Go files and building the interface definitions needed to generate mocks.
Package parser contains the parser for reading Go files and building the interface definitions needed to generate mocks.
mocks/interfacefilter
Code generated by Kelpie.
Code generated by Kelpie.
Package slices contains generic functions for working with slices.
Package slices contains generic functions for working with slices.

Jump to

Keyboard shortcuts

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