mocha

package module
Version: v1.1.0 Latest Latest
Warning

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

Go to latest
Published: Jul 24, 2022 License: MIT Imports: 22 Imported by: 0

README

Mocha

Mocha Logo

HTTP Mocking Tool for Go

Overview

HTTP server mocking tool for Go.
Mocha creates a real HTTP server and lets you configure response stubs for HTTP Requests when it matches a set of matchers. It provides a functional like API that allows you to match any part of a request against a set of matching functions that can be composed.

Inspired by WireMock and Nock.

Installation

go get github.com/vitorsalgado/mocha

Features

  • Configure HTTP response stubs for specific requests based on a criteria set.
  • Matches request URL, headers, queries, body.
  • Stateful matches to create scenarios, mocks for a specific number of calls.
  • Response body template.
  • Response delays.
  • Run in your automated tests.

How It Works

Mocha works by creating a real HTTP Server that you can configure response stubs for HTTP requests when they match a set request matchers. Mock definitions are stored in memory in the server and response will continue to be served as long as the requests keep passing the configured matchers.
The basic is workflow for a request is:

  • run configured middlewares
  • mocha parses the request body based on:
    • custom RequestBodyParser configured
    • request content-type
  • mock http handler tries to find a mock for the incoming request were all matchers evaluates to true
    • if a mock is found, it will run post matchers.
    • if all matchers passes, it will use mock reply implementation to build a response
    • if no mock is found, it returns an HTTP Status Code 418 (teapot).
  • after serving a mock response, it will run any core.PostAction configured.

Getting Started

Usage typically looks like the example below:

func Test_Example(t *testing.T) {
	m := mocha.New(t)
	m.Start()

	scoped := m.AddMocks(mocha.Get(expect.URLPath("/test")).
		Header("test", expect.ToEqual("hello")).
		Query("filter", expect.ToEqual("all")).
		Reply(reply.Created().BodyString("hello world")))

	req, _ := http.NewRequest(http.MethodGet, m.URL()+"/test?filter=all", nil)
	req.Header.Add("test", "hello")

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		t.Fatal(err)
	}

	body, err := ioutil.ReadAll(res.Body)

	assert.Nil(t, err)
	assert.True(t, scoped.Called())
	assert.Equal(t, 201, res.StatusCode)
	assert.Equal(t, string(body), "hello world")
}

Configuration

Mocha has two ways to create an instance: mocha.New() and mocha.NewSimple().
mocha.NewSimple() creates a new instance with default values for everything.
mocha.New(t, ...config) needs a core.T implementation and allows to configure the mock server. You use testing.T implementation. Mocha will use this to log useful information for each request match attempt. Use mocha.Configure() or provide a mocha.Config to configure the mock server.

Request Matching

Matchers can be applied to any part of a Request and Mocha provides a fluent API to make your life easier.
See usage examples below:

Method and URL

m := mocha.New(t)
m.AddMocks(mocha.Request().Method(http.MethodGet).URL(expect.URLPath("/test"))

Header

m := mocha.New(t)
m.AddMocks(mocha.Get(expect.URLPath("/test")).
    Header("test", expect.ToEqual("hello")))

Query

m := mocha.New(t)
m.AddMocks(mocha.Get(expect.URLPath("/test")).
    Query("filter", expect.ToEqual("all")))

Body

Matching JSON Fields

m := mocha.New(t)
m.AddMocks(mocha.Post(expect.URLPath("/test")).
    Body(
        expect.JSONPath("name", expect.ToEqual("dev")), expect.JSONPath("ok", expect.ToEqual(true))).
    Reply(reply.OK()))

Form URL Encoded Fields

m.AddMocks(mocha.Post(expect.URLPath("/test")).
    FormField("field1", expect.ToEqual("dev")).
    FormField("field2", expect.ToContain("qa")).
    Reply(reply.OK()))

Replies

You can define a response that should be served once a request is matched.
Mocha provides several ways to configure a reply.
The built-in reply features are:

Replies are based on the core.Reply interface.
It's also possible to configure response bodies from templates. Mocha uses Go Templates. Replies usage examples:

Basic Reply

m := mocha.New(t)
m.AddMocks(mocha.Get(expect.URLPath("/test")).
    Reply(reply.OK())

Replies In Sequence

m := mocha.New(t)
m.AddMocks(mocha.Get(expect.URLPath("/test")).
    Reply(reply.Seq().
	    Add(InternalServerError(), BadRequest(), OK(), NotFound())))

Random Replies

m := mocha.New(t)
m.AddMocks(mocha.Get(expect.URLPath("/test")).
    Reply(reply.Rand().
		Add(BadRequest(), OK(), Created(), InternalServerError())))

Reply Function

m := mocha.New(t)
m.AddMocks(mocha.Get(expect.URLPath("/test")).
    ReplyFunction(func(r *http.Request, m *core.Mock, p parameters.Params) (*core.Response, error) {
        return &core.Response{Status: http.StatusAccepted}, nil
    }))

Reply From Forwarded Request

reply.From will forward the request to the given destination and serve the response from the forwarded server.
It`s possible to add extra headers to the request and the response and also remove unwanted headers.

m := mocha.New(t)
m.AddMocks(mocha.Get(expect.URLPath("/test")).
    Reply(reply.From("http://example.org").
		ProxyHeader("x-proxy", "proxied").
		RemoveProxyHeader("x-to-be-removed").
		Header("x-res", "response"))

Body Template

Mocha comes with a built-in template parser based on Go Templates.
To serve a response body from a template, follow the example below:

templateFile, _ := os.Open("template.tmpl"))
content, _ := ioutil.ReadAll(templateFile)

m := mocha.New(t)
m.AddMocks(mocha.Get(expect.URLPath("/test")).
    Reply(reply.
        OK().
        BodyTemplate(reply.NewTextTemplate().
            FuncMap(template.FuncMap{"trim": strings.TrimSpace}).
            Template(string(content))).
        Model(data))

Specifying Headers

m := mocha.New(t)
m.AddMocks(mocha.Get(expect.URLPath("/test")).
    Reply(reply.OK().Header("test", "test-value"))

Delay Responses

You can configure a delay to responses to simulate timeouts, slow requests and any other timing related scenarios.
See the example below:

delay := time.Duration(1250) * time.Millisecond

m.AddMocks(Get(expect.URLPath("/test")).
    Reply(reply.
        OK().
        Delay(delay)))

Assertions

Mocha Instance

Mocha instance provides methods to assert if associated mocks were called or not, how many times they were called, allows you to enable/disable then and so on.
The available assertion methods on mocha instance are:

  • AssertCalled: asserts that all associated mocks were called at least once.
  • AssertNotCalled: asserts that associated mocks were not called.
  • AssertHits: asserts that the sum of calls is equal to the expected value.

Scope

Mocha instance method AddMocks returns a Scoped instance that holds all mocks created.
Scopes allows you control related mocks, enabling/disabling, checking if they were called or not. Scoped instance also provides assertions to facility tests verification. See below the available test assertions:

  • AssertCalled: asserts that all associated mocks were called at least once.
  • AssertNotCalled: asserts that associated mocks were not called.

Matchers

Mocha provides several matcher functions to facilitate request matching and verification. See the package expect for more details.
You can create custom matchers using these two approaches:

  • create a expect.Matcher struct
  • use the function expect.Func providing a function with the following signature: func(v any, a expect.Args) (bool, error)

BuiltIn Matchers

Matcher Description
AllOf Returns true when all given matchers returns true
AnyOf Returns true when any given matchers returns true
Both Returns true when both matchers returns true
ToContain Returns true when expected value is contained on the request value
Either Returns true when any matcher returns true
ToBeEmpty Returns true when request value is empty
ToEqual Returns true when values are equal
ToEqualFold Returns true when string values are equal, ignoring case
ToEqualJSON Returns true when the expected struct represents a JSON value
Func Wraps a function to create a inline matcher
ToHaveKey Returns true if the JSON key in the given path is present
ToHavePrefix Returns true if the matcher argument starts with the given prefix
ToHaveSuffix Returns true when matcher argument ends with the given suffix
JSONPath Applies the provided matcher to the JSON field value in the given path
ToHaveLen Returns true when matcher argument length is equal to the expected value
LowerCase Lower case matcher string argument before submitting it to provided matcher.
UpperCase Upper case matcher string argument before submitting it to provided matcher
ToMatchExpr Returns true then the given regular expression matches matcher argument
Not Negates the provided matcher
Peek Will return the result of the given matcher, after executing the provided function
ToBePresent Checks if matcher argument contains a value that is not nil or the zero value for the argument type
Repeat Returns true if total request hits for current mock is equal or lower total the provided max call times.
Trim Trims' spaces of matcher argument before submitting it to the given matcher
URLPath Returns true if request URL path is equal to the expected path, ignoring case
XOR Exclusive "or" matcher

Future Plans

  • Configure mocks with JSON/YAML files
  • CLI
  • Docker
  • Proxy and Record

Contributing

Check our Contributing guide for more details.

License

FOSSA Status

This project is MIT Licensed.

back to the top

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func NewStdoutNotifier

func NewStdoutNotifier() core.T

NewStdoutNotifier returns a core.T implementation that logs to the stdout. FailNow() and Helper() will do nothing.

Types

type Cleanable

type Cleanable interface {
	Cleanup(func())
}

Cleanable allows marking mocha instance to be closed on test cleanup.

type Config

type Config struct {
	// Context to be used internally by Mocha.
	Context context.Context

	// Addr defines a custom server address.
	Addr string

	// BodyParsers defines request body parsers to be executed before core parsers.
	BodyParsers []RequestBodyParser

	// Middlewares defines a list of custom middlewares that will be
	// set after panic recover and before mock handler.
	Middlewares []func(http.Handler) http.Handler

	// CORS defines CORS configurations.
	CORS cors.Config

	// Server defines a custom mock HTTP server.
	Server Server

	// LogVerbosity defines the level of logs
	LogVerbosity LogVerbosity
	// contains filtered or unexported fields
}

Config holds Mocha mock server configurations.

type Configurer

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

Configurer is Config builder, Use this to build Mocha options, instead of creating a new Config struct manually.

func Configure

func Configure() *Configurer

Configure inits a new Configurer. Entrypoint to start a new custom configuration for Mocha mock servers.

func (*Configurer) Addr

func (cb *Configurer) Addr(addr string) *Configurer

Addr sets a custom address for the mock HTTP server.

func (*Configurer) Build

func (cb *Configurer) Build() Config

Build builds a new Config with previously configured values.

func (*Configurer) CORS

func (cb *Configurer) CORS(options ...cors.Config) *Configurer

CORS configures CORS for the mock server.

func (*Configurer) Context

func (cb *Configurer) Context(context context.Context) *Configurer

Context sets a custom context.

func (*Configurer) LogVerbosity

func (cb *Configurer) LogVerbosity(l LogVerbosity) *Configurer

LogVerbosity configure the verbosity of informative logs. Defaults to LogVerbose.

func (*Configurer) Middlewares

func (cb *Configurer) Middlewares(fn ...func(handler http.Handler) http.Handler) *Configurer

Middlewares adds custom middlewares to the mock server. Use this to add custom request interceptors.

func (*Configurer) RequestBodyParsers

func (cb *Configurer) RequestBodyParsers(bp ...RequestBodyParser) *Configurer

RequestBodyParsers adds a custom list of RequestBodyParsers.

func (*Configurer) Server

func (cb *Configurer) Server(srv Server) *Configurer

Server configures a custom HTTP mock Server.

type LogVerbosity

type LogVerbosity int
const (
	LogSilently LogVerbosity = iota
	LogVerbose
)

Log verbosity enum

type Mocha

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

Mocha is the base for the mock server.

func New

func New(t core.T, config ...Config) *Mocha

New creates a new Mocha mock server with the given configurations. Parameter config accepts a Config or a Configurer implementation.

func NewBasic

func NewBasic() *Mocha

NewBasic creates a new Mocha mock server with default configurations.

func (*Mocha) AddMocks

func (m *Mocha) AddMocks(builders ...*MockBuilder) *Scoped

AddMocks adds one or multiple request mocks. It returns a Scoped instance that allows control of the added mocks and also checking if they were called or not. The returned Scoped is useful for tests.

Usage:

scoped := m.AddMocks(
	Get(expect.URLPath("/test")).
		Header("test", expect.ToEqual("hello")).
		Query("filter", expect.ToEqual("all")).
		Reply(reply.Created().BodyString("hello world")))

assert.True(t, scoped.Called())

func (*Mocha) AssertCalled

func (m *Mocha) AssertCalled(t core.T) bool

AssertCalled asserts that all mocks associated with this instance were called at least once.

func (*Mocha) AssertHits

func (m *Mocha) AssertHits(t core.T, expected int) bool

AssertHits asserts that the sum of request hits for mocks is equal to the given expected value.

func (*Mocha) AssertNotCalled

func (m *Mocha) AssertNotCalled(t core.T) bool

AssertNotCalled asserts that all mocks associated with this instance were called at least once.

func (*Mocha) Close

func (m *Mocha) Close() error

Close closes the mock server.

func (*Mocha) CloseOnCleanup

func (m *Mocha) CloseOnCleanup(t Cleanable) *Mocha

CloseOnCleanup adds mocha server Close to the Cleanup.

func (*Mocha) Disable

func (m *Mocha) Disable()

Disable disables all mocks.

func (*Mocha) Enable

func (m *Mocha) Enable()

Enable disables all mocks.

func (*Mocha) Hits

func (m *Mocha) Hits() int

Hits returns the total request hits.

func (*Mocha) Parameters

func (m *Mocha) Parameters() parameters.Params

Parameters allows managing custom parameters that will be available inside matchers.

func (*Mocha) Start

func (m *Mocha) Start() ServerInfo

Start starts the mock server.

func (*Mocha) StartTLS

func (m *Mocha) StartTLS() ServerInfo

StartTLS starts TLS from a server.

func (*Mocha) Subscribe

func (m *Mocha) Subscribe(evt events.Events)

Subscribe add a new event listener.

func (*Mocha) URL

func (m *Mocha) URL() string

URL returns the base URL string for the mock server.

type MockBuilder

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

MockBuilder is a builder for mock.Mock.

func Delete

func Delete(m expect.Matcher) *MockBuilder

Delete inits a mock for Delete method.

func Get

func Get(m expect.Matcher) *MockBuilder

Get inits a mock for GET method.

func Head(m expect.Matcher) *MockBuilder

Head inits a mock for Head method.

func Options

func Options(m expect.Matcher) *MockBuilder

Options inits a mock for Options method.

func Patch

func Patch(u expect.Matcher) *MockBuilder

Patch inits a mock for Patch method.

func Post

func Post(m expect.Matcher) *MockBuilder

Post inits a mock for Post method.

func Put

func Put(m expect.Matcher) *MockBuilder

Put inits a mock for Put method.

func Request

func Request() *MockBuilder

Request creates a new empty MockBuilder.

func (*MockBuilder) Body

func (b *MockBuilder) Body(matcherList ...expect.Matcher) *MockBuilder

Body adds matchers to the request body. If request contains a JSON body, you can provide multiple matchers to several fields. Example:

m.Body(JSONPath("name", EqualTo("test")), JSONPath("address.street", ToContains("nowhere")))

func (*MockBuilder) Build

func (b *MockBuilder) Build() *core.Mock

Build builds a mock.Mock with previously configured parameters. Used internally by Mocha.

func (*MockBuilder) FormField

func (b *MockBuilder) FormField(field string, m expect.Matcher) *MockBuilder

FormField defines a matcher for a specific form field by its key.

func (*MockBuilder) Header

func (b *MockBuilder) Header(key string, m expect.Matcher) *MockBuilder

Header adds a matcher to a specific http.Header key.

func (*MockBuilder) MatchAfter

func (b *MockBuilder) MatchAfter(m expect.Matcher) *MockBuilder

MatchAfter adds a expect.Matcher that will run after the standard matchers and before serving the mocked response. After matchers are mostly used in special cases, like when they need to keep data that should not be evaluated all the time.

func (*MockBuilder) Method

func (b *MockBuilder) Method(method string) *MockBuilder

Method sets the HTTP request method to be matched.

func (*MockBuilder) Name

func (b *MockBuilder) Name(name string) *MockBuilder

Name defines a name for the mock. Useful to debug.

func (*MockBuilder) PostAction

func (b *MockBuilder) PostAction(action core.PostAction) *MockBuilder

PostAction adds a post action to be executed after the mocked response is served.

func (*MockBuilder) Priority

func (b *MockBuilder) Priority(p int) *MockBuilder

Priority sets the priority of the mock. A higher priority will take precedence during request matching.

func (*MockBuilder) Query

func (b *MockBuilder) Query(key string, m expect.Matcher) *MockBuilder

Query defines a matcher to a specific query.

func (*MockBuilder) Repeat

func (b *MockBuilder) Repeat(times int) *MockBuilder

Repeat defines to total times that a mock should be served, if request matches.

func (*MockBuilder) Reply

func (b *MockBuilder) Reply(rep core.Reply) *MockBuilder

Reply defines a response mock to be served if this mock matches to a request.

func (*MockBuilder) ReplyFunction

func (b *MockBuilder) ReplyFunction(fn func(*http.Request, *core.Mock, parameters.Params) (*core.Response, error)) *MockBuilder

ReplyFunction defines a function to will build the response mock.

func (*MockBuilder) ReplyJust

func (b *MockBuilder) ReplyJust(status int, r ...reply.StdReply) *MockBuilder

ReplyJust sets the mock to return a simple response with the given status code. Optionally, you can provide a reply as well. The status provided in the first parameter will prevail. Only the first reply will be used.

func (*MockBuilder) RequestMatches

func (b *MockBuilder) RequestMatches(m expect.Matcher) *MockBuilder

RequestMatches defines expect.Matcher to be applied to a http.Request.

func (*MockBuilder) ScenarioIs

func (b *MockBuilder) ScenarioIs(scenario string) *MockBuilder

ScenarioIs mark this mock to be used only within the given scenario.

func (*MockBuilder) ScenarioStateIs

func (b *MockBuilder) ScenarioStateIs(requiredState string) *MockBuilder

ScenarioStateIs mark this mock to be served only if the scenario state is equal to the given required state.

func (*MockBuilder) ScenarioStateWillBe

func (b *MockBuilder) ScenarioStateWillBe(newState string) *MockBuilder

ScenarioStateWillBe defines the state of the scenario after this mock is matched, making the scenario flow continue.

func (*MockBuilder) StartScenario

func (b *MockBuilder) StartScenario(name string) *MockBuilder

StartScenario sets that this mock will start a new scenario with the given name.

func (*MockBuilder) URL

URL defines a matcher to be applied to the http.Request url.URL.

type RequestBodyParser

type RequestBodyParser interface {
	// CanParse checks if current request body should be parsed by this component.
	// First parameter is the incoming content-type.
	CanParse(contentType string, r *http.Request) bool

	// Parse parses the request body.
	Parse(body []byte, r *http.Request) (any, error)
}

RequestBodyParser parses request body if CanParse returns true. Multiple implementations of RequestBodyParser can be provided to Mocha using options.

type Scoped

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

Scoped holds references to one or more added mocks allowing users perform operations on them, like enabling/disabling.

func (*Scoped) AssertCalled

func (s *Scoped) AssertCalled(t core.T) bool

AssertCalled reports an error if there are still pending mocks.

func (*Scoped) AssertNotCalled

func (s *Scoped) AssertNotCalled(t core.T) bool

AssertNotCalled reports a error if any mock was called.

func (*Scoped) Called

func (s *Scoped) Called() bool

Called returns true if all scoped mocks were called at least once.

func (*Scoped) Clean

func (s *Scoped) Clean()

Clean all scoped mocks.

func (*Scoped) Disable

func (s *Scoped) Disable()

Disable scoped mocks. Disabled mocks will be ignored.

func (*Scoped) Enable

func (s *Scoped) Enable()

Enable scoped mocks.

func (*Scoped) Get

func (s *Scoped) Get(id int) *core.Mock

Get returns a mock with the provided id.

func (*Scoped) Hits

func (s *Scoped) Hits() int

Hits returns the sum of the scoped mocks calls.

func (*Scoped) IsPending

func (s *Scoped) IsPending() bool

IsPending returns true when there are one or more mocks that were not called at least once.

func (*Scoped) ListAll

func (s *Scoped) ListAll() []*core.Mock

ListAll returns all mocks scoped in this instance.

func (*Scoped) ListCalled

func (s *Scoped) ListCalled() []*core.Mock

ListCalled returns all mocks that were called.

func (*Scoped) ListPending

func (s *Scoped) ListPending() []*core.Mock

ListPending returns all mocks that were not called at least once.

type Server

type Server interface {
	// Configure configures the HTTP mock.
	// It is the first method called by Mocha during initialization.
	Configure(Config, http.Handler) error

	// Start starts a server.
	Start() (ServerInfo, error)

	// StartTLS starts a TLS server.
	StartTLS() (ServerInfo, error)

	// Close the server.
	Close() error

	// Info returns server information.
	Info() ServerInfo
}

Server defines HTTP mock server operations.

type ServerInfo

type ServerInfo struct {
	URL string
}

ServerInfo holds HTTP server information, like its URL.

type StdoutNotifier

type StdoutNotifier struct {
}

StdoutNotifier implements core.T outputting logs to the stdout.

func (*StdoutNotifier) Errorf

func (n *StdoutNotifier) Errorf(format string, args ...any)

func (*StdoutNotifier) FailNow

func (n *StdoutNotifier) FailNow()

FailNow do nothing.

func (*StdoutNotifier) Helper

func (n *StdoutNotifier) Helper()

Helper do nothing.

func (*StdoutNotifier) Logf

func (n *StdoutNotifier) Logf(format string, args ...any)

Directories

Path Synopsis
_examples
Package core is the hearth of Mocha.
Package core is the hearth of Mocha.
Package expect implements several Matcher functions to facilitate matching HTTP request parameters.
Package expect implements several Matcher functions to facilitate matching HTTP request parameters.
Package feat implements additional features for the mock server.
Package feat implements additional features for the mock server.
cors
Package cors implements functions to enable New support for the mock server.
Package cors implements functions to enable New support for the mock server.
events
Package events implements event listeners for mocha internal actions.
Package events implements event listeners for mocha internal actions.
Package reply implements different response definition builders.
Package reply implements different response definition builders.
x
headers
Package headers contains common headers used internally.
Package headers contains common headers used internally.
jsonx
Package jsonx implements functions to get JSON property values by their path.
Package jsonx implements functions to get JSON property values by their path.
mimetypes
Package mimetypes contains common mime types used internally by Mocha.
Package mimetypes contains common mime types used internally by Mocha.
internal
colorize
Package colorize contains functions to stylize texts for terminal.
Package colorize contains functions to stylize texts for terminal.
middleware
Package middleware contains utilities to handle middlewares and also common middlewares used internally by Mocha.
Package middleware contains utilities to handle middlewares and also common middlewares used internally by Mocha.
parameters
Package parameters implements a simple in-memory key/value store, used internally by Mocha.
Package parameters implements a simple in-memory key/value store, used internally by Mocha.
testutil
Package testutil contains internal test utilities.
Package testutil contains internal test utilities.

Jump to

Keyboard shortcuts

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