pub struct Mock { /* private fields */ }
Expand description
Given a set of matchers, a Mock
instructs an instance of MockServer
to return a pre-determined response if the matching conditions are satisfied.
Mock
s have to be mounted (or registered) with a MockServer
to become effective.
You can use:
MockServer::register
orMock::mount
to activate a globalMock
;MockServer::register_as_scoped
orMock::mount_as_scoped
to activate a scopedMock
.
Check the respective documentations for more details (or look at the following examples!).
§Example (using register
):
use wiremock::{MockServer, Mock, ResponseTemplate};
use wiremock::matchers::method;
#[async_std::main]
async fn main() {
// Arrange
let mock_server = MockServer::start().await;
let response = ResponseTemplate::new(200);
let mock = Mock::given(method("GET")).respond_with(response.clone());
// Registering the mock with the mock server - it's now effective!
mock_server.register(mock).await;
// We won't register this mock instead.
let unregistered_mock = Mock::given(method("POST")).respond_with(response);
// Act
let status = surf::get(&mock_server.uri())
.await
.unwrap()
.status();
assert_eq!(status, 200);
// This would have matched `unregistered_mock`, but we haven't registered it!
// Hence it returns a 404, the default response when no mocks matched on the mock server.
let status = surf::post(&mock_server.uri())
.await
.unwrap()
.status();
assert_eq!(status, 404);
}
§Example (using mount
):
If you prefer a fluent style, you can use the mount
method on the Mock
itself
instead of register
.
use wiremock::{MockServer, Mock, ResponseTemplate};
use wiremock::matchers::method;
#[async_std::main]
async fn main() {
// Arrange
let mock_server = MockServer::start().await;
Mock::given(method("GET"))
.respond_with(ResponseTemplate::new(200))
.up_to_n_times(1)
// Mounting the mock on the mock server - it's now effective!
.mount(&mock_server)
.await;
// Act
let status = surf::get(&mock_server.uri())
.await
.unwrap()
.status();
assert_eq!(status, 200);
}
§Example (using mount_as_scoped
):
Sometimes you will need a Mock
to be active within the scope of a function, but not any longer.
You can use Mock::mount_as_scoped
to precisely control how long a Mock
stays active.
use wiremock::{MockServer, Mock, ResponseTemplate};
use wiremock::matchers::method;
async fn my_test_helper(mock_server: &MockServer) {
let mock_guard = Mock::given(method("GET"))
.respond_with(ResponseTemplate::new(200))
.expect(1)
.named("my_test_helper GET /")
.mount_as_scoped(mock_server)
.await;
surf::get(&mock_server.uri())
.await
.unwrap();
// `mock_guard` is dropped, expectations are verified!
}
#[async_std::main]
async fn main() {
// Arrange
let mock_server = MockServer::start().await;
my_test_helper(&mock_server).await;
// Act
// This would have returned 200 if the `Mock` in
// `my_test_helper` had not been scoped.
let status = surf::get(&mock_server.uri())
.await
.unwrap()
.status();
assert_eq!(status, 404);
}
Implementations§
source§impl Mock
impl Mock
sourcepub fn given<M: 'static + Match>(matcher: M) -> MockBuilder
pub fn given<M: 'static + Match>(matcher: M) -> MockBuilder
Start building a Mock
specifying the first matcher.
It returns an instance of MockBuilder
.
sourcepub fn up_to_n_times(self, n: u64) -> Mock
pub fn up_to_n_times(self, n: u64) -> Mock
Specify an upper limit to the number of times you would like this Mock
to respond to
incoming requests that satisfy the conditions imposed by your matchers
.
§Example:
use wiremock::{MockServer, Mock, ResponseTemplate};
use wiremock::matchers::method;
#[async_std::main]
async fn main() {
// Arrange
let mock_server = MockServer::start().await;
Mock::given(method("GET"))
.respond_with(ResponseTemplate::new(200))
// Default behaviour will have this Mock responding to any incoming request
// that satisfied our matcher (e.g. being a GET request).
// We can opt out of the default behaviour by setting a cap on the number of
// matching requests this Mock should respond to.
//
// In this case, once one matching request has been received, the mock will stop
// matching additional requests and you will receive a 404 if no other mock
// matches on those requests.
.up_to_n_times(1)
.mount(&mock_server)
.await;
// Act
// The first request matches, as expected.
let status = surf::get(&mock_server.uri())
.await
.unwrap()
.status();
assert_eq!(status, 200);
// The second request does NOT match given our `up_to_n_times(1)` setting.
let status = surf::get(&mock_server.uri())
.await
.unwrap()
.status();
assert_eq!(status, 404);
}
sourcepub fn with_priority(self, p: u8) -> Mock
pub fn with_priority(self, p: u8) -> Mock
Specify a priority for this Mock
.
Use this when you mount many Mock
in a MockServer
and those mocks have interlaced request matching conditions
e.g. mock A
accepts path /abcd
and mock B
a path regex [a-z]{4}
It is recommended to set the highest priority (1) for mocks with exact conditions (mock A
in this case)
1
is the highest priority, 255
the lowest, default to 5
If two mocks have the same priority, priority is defined by insertion order (first one mounted has precedence over the others).
§Example:
use wiremock::{MockServer, Mock, ResponseTemplate};
use wiremock::matchers::{method, path, path_regex};
#[async_std::main]
async fn main() {
// Arrange
let mock_server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("abcd"))
.respond_with(ResponseTemplate::new(200))
.with_priority(1) // highest priority
.mount(&mock_server)
.await;
Mock::given(method("GET"))
.and(path_regex("[a-z]{4}"))
.respond_with(ResponseTemplate::new(201))
.with_priority(2)
.mount(&mock_server)
.await;
// Act
// The request with highest priority, as expected.
let status = surf::get(&format!("{}/abcd", mock_server.uri()))
.await
.unwrap()
.status();
assert_eq!(status, 200);
}
sourcepub fn expect<T: Into<Times>>(self, r: T) -> Self
pub fn expect<T: Into<Times>>(self, r: T) -> Self
Set an expectation on the number of times this Mock
should match in the current
test case.
Expectations are verified when the MockServer
is shutting down: if the expectation
is not satisfied, the MockServer
will panic and the error_message
is shown.
By default, no expectation is set for Mock
s.
§When is this useful?
expect
can turn out handy when you’d like to verify that a certain side-effect has
(or has not!) taken place.
For example:
- check that a 3rd party notification API (e.g. email service) is called when an event in your application is supposed to trigger a notification;
- check that a 3rd party API is NOT called when the response of a call is expected
to be retrieved from a cache (
.expect(0)
).
This technique is also called spying.
§Example:
use wiremock::{MockServer, Mock, ResponseTemplate};
use wiremock::matchers::method;
#[async_std::main]
async fn main() {
// Arrange
let mock_server = MockServer::start().await;
Mock::given(method("GET"))
.respond_with(ResponseTemplate::new(200))
.up_to_n_times(2)
// We expect the mock to be called at least once.
// If that does not happen, the `MockServer` will panic on shutdown,
// causing the whole test to fail.
.expect(1..)
// We assign a name to the mock - it will be shown in error messages
// if our expectation is not verified!
.named("Root GET")
.mount(&mock_server)
.await;
// Act
let status = surf::get(&mock_server.uri())
.await
.unwrap()
.status();
assert_eq!(status, 200);
// Assert
// We made at least one matching request, the expectation is satisfied.
// The `MockServer` will shutdown peacefully, without panicking.
}
sourcepub fn named<T: Into<String>>(self, mock_name: T) -> Self
pub fn named<T: Into<String>>(self, mock_name: T) -> Self
Assign a name to your mock.
The mock name will be used in error messages (e.g. if the mock expectation is not satisfied) and debug logs to help you identify what failed.
§Example:
use wiremock::{MockServer, Mock, ResponseTemplate};
use wiremock::matchers::method;
#[async_std::main]
async fn main() {
// Arrange
let mock_server = MockServer::start().await;
// We have two mocks in the same test - how do we find out
// which one failed when the test panics?
// Assigning a name to each mock with `named` gives us better error
// messages and makes it much easier to debug why a test is failing!
Mock::given(method("GET"))
.respond_with(ResponseTemplate::new(200))
.up_to_n_times(2)
.expect(1..)
// We assign a name to the mock - it will be shown in error messages
// if our expectation is not verified!
.named("Root GET")
.mount(&mock_server)
.await;
Mock::given(method("POST"))
.respond_with(ResponseTemplate::new(200))
.up_to_n_times(2)
.expect(1..)
// We assign a name to the mock - it will be shown in error messages
// if our expectation is not verified!
.named("Root POST")
.mount(&mock_server)
.await;
// Act
let status = surf::get(&mock_server.uri())
.await
.unwrap()
.status();
assert_eq!(status, 200);
// Assert
// We did not make a POST request, therefore the expectation on `Root POST`
// is not satisfied and the test will panic.
}
sourcepub async fn mount(self, server: &MockServer)
pub async fn mount(self, server: &MockServer)
Mount a Mock
on an instance of MockServer
.
The Mock
will remain active until MockServer
is shut down. If you want to control or limit how
long your Mock
stays active, check out Mock::mount_as_scoped
.
Be careful! Mock
s are not effective until they are mount
ed or register
ed on a MockServer
.
mount
is an asynchronous method, make sure to .await
it!
sourcepub async fn mount_as_scoped(self, server: &MockServer) -> MockGuard
pub async fn mount_as_scoped(self, server: &MockServer) -> MockGuard
Mount a Mock
as scoped on an instance of MockServer
.
When using mount
, your Mock
s will be active until the MockServer
is shut down.
When using mount_as_scoped
, your Mock
s will be active as long as the returned MockGuard
is not dropped.
When the returned MockGuard
is dropped, MockServer
will verify that the expectations set on the scoped Mock
were
verified - if not, it will panic.
mount_as_scoped
is the ideal solution when you need a Mock
within a test helper
but you do not want it to linger around after the end of the function execution.
§Limitations
When expectations of a scoped Mock
are not verified, it will trigger a panic - just like a normal Mock
.
Due to limitations in Rust’s Drop
trait,
the panic message will not include the filename and the line location
where the corresponding MockGuard
was dropped - it will point into wiremock
’s source code.
This can be an issue when you are using more than one scoped Mock
in a single test - which of them panicked?
To improve your debugging experience it is strongly recommended to use Mock::named
to assign a unique
identifier to your scoped Mock
s, which will in turn be referenced in the panic message if their expectations are
not met.
§Example:
- The behaviour of the scoped mock is invisible outside of
my_test_helper
.
use wiremock::{MockServer, Mock, ResponseTemplate};
use wiremock::matchers::method;
async fn my_test_helper(mock_server: &MockServer) {
let mock_guard = Mock::given(method("GET"))
.respond_with(ResponseTemplate::new(200))
.expect(1)
.named("my_test_helper GET /")
.mount_as_scoped(mock_server)
.await;
surf::get(&mock_server.uri())
.await
.unwrap();
// `mock_guard` is dropped, expectations are verified!
}
#[async_std::main]
async fn main() {
// Arrange
let mock_server = MockServer::start().await;
my_test_helper(&mock_server).await;
// Act
// This would have returned 200 if the `Mock` in
// `my_test_helper` had not been scoped.
let status = surf::get(&mock_server.uri())
.await
.unwrap()
.status();
assert_eq!(status, 404);
}
- The expectations for the scoped mock are not verified, it panics at the end of
my_test_helper
.
use wiremock::{MockServer, Mock, ResponseTemplate};
use wiremock::matchers::method;
async fn my_test_helper(mock_server: &MockServer) {
let mock_guard = Mock::given(method("GET"))
.respond_with(ResponseTemplate::new(200))
.expect(1)
.named("my_test_helper GET /")
.mount_as_scoped(mock_server)
.await;
// `mock_guard` is dropped, expectations are NOT verified!
// Panic!
}
#[async_std::main]
async fn main() {
// Arrange
let mock_server = MockServer::start().await;
my_test_helper(&mock_server).await;
// Act
let status = surf::get(&mock_server.uri())
.await
.unwrap()
.status();
assert_eq!(status, 404);
}