Wiremock: async HTTP mocking to test Rust applications

栏目: IT技术 · 发布时间: 4年前

内容简介:I releasedThe nameYou can spin up as many mock HTTP servers as you need to mock all the 3rd party APIs that your application interacts with.

TL;DR

I released wiremock , a new crate that provides HTTP mocking to test Rust applications.

 1use wiremock::{MockServer, Mock, ResponseTemplate};
 2use wiremock::matchers::{method, path};
 3
 4#[async_std::main]
 5async fn main() {
 6    // Start a background HTTP server on a random local port
 7    let mock_server = MockServer::start().await;
 8
 9    // Arrange the behaviour of the MockServer adding a Mock:
10    // when it receives a GET request on '/hello' it will respond with a 200.
11    Mock::given(method("GET"))
12        .and(path("/hello"))
13        .respond_with(ResponseTemplate::new(200))
14        // Mounting the mock on the mock server - it's now effective!
15        .mount(&mock_server)
16        .await;
17    
18    // If we probe the MockServer using any HTTP client it behaves as expected.
19    let status = surf::get(format!("{}/hello", &mock_server.uri()))
20        .await
21        .unwrap()
22        .status();
23    assert_eq!(status.as_u16(), 200);
24
25    // If the request doesn't match any `Mock` mounted on our `MockServer` 
26    // a 404 is returned.
27    let status = surf::get(format!("{}/missing", &mock_server.uri()))
28        .await
29        .unwrap()
30        .status();
31    assert_eq!(status.as_u16(), 404);
32}
33

The name wiremock is a reference to WireMock.Net , a .NET port of the original Wiremock from Java.

You can spin up as many mock HTTP servers as you need to mock all the 3rd party APIs that your application interacts with.

Mock HTTP servers are fully isolated: tests can be run in parallel, with no interference. Each server is shut down when it goes out of scope (e.g. end of test execution).

wiremock provides out-of-the-box a set of matching strategies, but more can be defined to suit your testing needs via the Match trait.

wiremock is asynchronous: it is compatible (and tested) against both async_std and tokio as runtimes.

Why did we need another HTTP mocking crate?

Wiremock: async HTTP mocking to test Rust applications

The backstory

I spent part of last week working on a project that might lead to the first Rust REST API in production at my current company.

As it happens when you have been advocating for a technology, it was not enough to get the job done.

I set out to write the best possible sample : a showcase of what an idiomatic Rust API should look like when it comes to logging, error handling, metrics, testing, domain modelling, etc. - a code base my colleagues could use as a reference if they were to choose Rust for a future project.

Wiremock: async HTTP mocking to test Rust applications

All in all things went pretty smoothly - everything I needed was available as a somewhat polished crate, already provided by the Rust community.

Everything but one key piece: HTTP mocking.

The problem

It is extremely valuable to have a set of tests in your suite that interact with your service via its public API, as a user, without any knowledge of its inner workings.

These API tests can be used to verify acceptance criteria for user stories and prevent regressions.

They are as well generally easier to maintain: they are fairly decoupled from the implementation details, with a focus on the behaviour that is visible to a user of the API.

The caveat: no API is an island, especially in a microservice architecture - your service is likely to interact with the public APIs of a number of external dependencies.

When performing API tests you generally do not want to spin up those external dependencies in your continuous integration pipeline - if your microservice architecture is intricate enough, you might end spinning up tens of services to test a tiny piece of functionality in your API under test. The setup alone could take a significant amount of time - best to run those tests in a test cluster, as a final pre-deployment check.

Furthermore, you might simply not be able to spin up some of those dependencies (e.g. 3rd party SaaS services).

For our run-it-on-every-commit CI pipeline, we can get away with most of the value without putting in a crazy amount of effort: we can use HTTP mocking .

We spin up an HTTP server in the background for each of our tests, define a set of request-response scenarios ( return response A if you receive request B ) and then configure our application to use the mock server in lieu of the real service.

The available options

Two existing crates might have provided what I needed: mockito and httpmock .

Unfortunately, they both suffer by a set of limitations which I didn’t want to live with:

  • You can only run a single mock server, hence you can only mock a single external API;
  • Tests must be run sequentially;
  • No way to define custom request matchers to extend the functionality provided out of the box by the crate.

Easter, 4 days-long break - I set out to write a new HTTP mocking crate, wiremock .

Wiremock: async HTTP mocking to test Rust applications

Wiremock

wiremock provides fully-isolated MockServer s : start takes care of finding a random port available on your local machine which is assigned to the new server.

You can use one instance of MockServer for each test and for each 3rd party API you need to mock - each server is shut down when it goes out of scope (e.g. end of test execution).

 1use wiremock::{MockServer, Mock, ResponseTemplate};
 2use wiremock::matchers::method;
 3
 4#[async_std::main]
 5async fn main() {
 6    // Arrange
 7    let mock_server_one = MockServer::start().await;
 8    let mock_server_two = MockServer::start().await;
 9
10    assert!(mock_server_one.address() != mock_server_two.address());
11
12    let mock = Mock::given(method("GET")).respond_with(ResponseTemplate::new(200));
13    // Registering the mock with the first mock server - it's now effective!
14    // But it *won't* be used by the second mock server!
15    mock_server_one.register(mock).await;
16
17    // It matches our mock
18    let status = surf::get(&mock_server_one.uri())
19        .await
20        .unwrap()
21        .status();
22    assert_eq!(status.as_u16(), 200);
23
24    // This would have matched our mock, but we haven't registered it 
25    // for `mock_server_two`!
26    // Hence it returns a 404, the default response when 
27    // no mocks matched on the mock server.
28    let status = surf::get(&mock_server_two.uri())
29        .await
30        .unwrap()
31        .status();
32    assert_eq!(status.as_u16(), 404);
33}
34

wiremock provides a set of matching strategies out of the box - see the matchers module for a complete list.

You can also define your own matchers using the Match trait, as well as using Fn closures.

How does it work?

Each instance of MockServer is, under the hood, a pair of actors running on Bastion :

When a request hits a MockServer :

  • the server actor tries to parse it from a async_std::net::TcpStream using async-h1 ;
  • the parsed request is passed as a message to the mock actor;
  • the mock actor checks all mocks against it, one by one - if one matches, the associated response is returned; if nothing matches a 404 is returned;
  • the server actor responds to the caller.

The two actors are killed when MockServer is dropped.

Going forward

Well, testing for the upcoming Rust API is surely going to be shiny!

For the overall Rust community, I foresee two main development directions when it comes to wiremock :

  • Spying is the biggest piece of functionality wiremock is currently missing: being able to assert if a mock matched, or how many times it did.
    Quite useful when you want to verify that certain side-effects have been triggered by your application.
  • More matching strategies for common use cases will make their way into wiremock as time goes forward.

以上所述就是小编给大家介绍的《Wiremock: async HTTP mocking to test Rust applications》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

The Little Schemer

The Little Schemer

[美] Daniel P. Friedman、[美] Matthias Felleisen / 卢俊祥 / 电子工业出版社 / 2017-7 / 65.00

《The Little Schemer:递归与函数式的奥妙》是一本久负盛名的经典之作,两位作者Daniel P. Friedman、Matthias Felleisen在程序语言界名声显赫。《The Little Schemer:递归与函数式的奥妙》介绍了Scheme的基本结构及其应用、Scheme的五法十诫、Continuation-Passing-Style、Partial Function、......一起来看看 《The Little Schemer》 这本书的介绍吧!

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具