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.
go get github.com/vitorsalgado/mocha/v3
- 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.
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
- custom
- mock http handler tries to find a mock for the incoming request were all matchers evaluates to true
- 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
PostAction
configured.
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")
}
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 mocha.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.
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:
m := mocha.New(t)
m.AddMocks(mocha.Request().Method(http.MethodGet).URL(expect.URLPath("/test"))
m := mocha.New(t)
m.AddMocks(mocha.Get(expect.URLPath("/test")).
Header("test", expect.ToEqual("hello")))
m := mocha.New(t)
m.AddMocks(mocha.Get(expect.URLPath("/test")).
Query("filter", expect.ToEqual("all")))
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()))
m.AddMocks(mocha.Post(expect.URLPath("/test")).
FormField("field1", expect.ToEqual("dev")).
FormField("field2", expect.ToContain("qa")).
Reply(reply.OK()))
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:
- Basic
- Random Replies
- Sequence Replies
- Function
- Reply From Proxied Forwarded Request
- Specify Headers
- Delay Responses
Replies are based on the Reply
interface.
It's also possible to configure response bodies from templates. Mocha uses Go Templates.
Replies usage examples:
m := mocha.New(t)
m.AddMocks(mocha.Get(expect.URLPath("/test")).
Reply(reply.OK())
m := mocha.New(t)
m.AddMocks(mocha.Get(expect.URLPath("/test")).
Reply(reply.Seq().
Add(InternalServerError(), BadRequest(), OK(), NotFound())))
m := mocha.New(t)
m.AddMocks(mocha.Get(expect.URLPath("/test")).
Reply(reply.Rand().
Add(reply.BadRequest(), reply.OK(), reply.Created(), reply.InternalServerError())))
Reply Function
m := mocha.New(t)
m.AddMocks(mocha.Get(expect.URLPath("/test")).
ReplyFunction(func(r *http.Request, m mocha.M, p params.P) (*mocha.Response, error) {
return &mocha.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"))
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))
m := mocha.New(t)
m.AddMocks(mocha.Get(expect.URLPath("/test")).
Reply(reply.OK().Header("test", "test-value"))
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)))
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.
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.
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)
It's possible to compose multiple matchers.
Every matcher has a .And()
, .Or()
and a .Xor()
that allows composing multiple matchers.
See the example below:
expect.ToEqual("test").And(expect.ToContain("t"))
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 |
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 |
- Configure mocks with JSON/YAML files
- CLI
- Docker
- Proxy and Record
Check our Contributing guide for more details.
This project is MIT Licensed.