Flutter本地模拟服务器插件mock_web_server的使用
Flutter本地模拟服务器插件mock_web_server的使用
mock_web_server
是一个灵活的 Dart Web 服务器,可以用于编写测试脚本和与 Web 服务器的交互。
概述
通过 mock_web_server
,您可以轻松地进行集成测试或重现特定的边缘情况。它允许您编写软件正在测试中的交互,以模拟 Web 服务器的行为。
MockWebServer
基于 Square 公司为 Java 创建的同名库。
使用方法
启动服务器
MockWebServer
可以运行在指定的端口或系统选择的临时端口上。
// 将使用系统选择的临时端口
new MockWebServer();
// 将使用端口 8081
new MockWebServer(port: 8081);
启动服务器只需执行以下操作:
MockWebServer server = new MockWebServer();
server.start();
一旦服务器运行起来,您可以获取其 URL 以便配置您的应用程序连接到该服务器。
server.url; // 返回 http://127.0.0.1:8081
server.port; // 8081
server.host; // 127.0.0.1
添加响应到队列
服务器启动后,您可以将响应添加到队列中。队列遵循先进先出(FIFO)原则。
// 添加空体并设置响应码为 401
server.enqueue(httpCode: 401);
// 设置响应码为 200,并添加 JSON 作为响应体
server.enqueue(body: '{ "message" : "hi"}');
// HTTP 200 响应,空体及一些头部信息
Map<String, String> headers = new Map();
headers["X-Server"] = "MockDart";
server.enqueue(headers: headers);
// 所有参数都是可选的,可以根据需要组合使用
server.enqueue(httpCode: 201, body: "answer", headers: headers, duration: duration);
// 可以直接调用 enqueueResponse() 方法来添加 MockResponse
Map<String, String> headers = new Map();
headers["X-Server"] = "MockDart";
var mockResponse = new MockResponse()
..httpCode = 201
..body = "Created"
..headers = headers
..delay = new Duration(seconds: 2);
server.enqueueResponse(mockResponse);
延迟响应
为了测试超时或竞态条件,您可以让服务器延迟响应。
server.enqueue(delay: new Duration(seconds: 2), httpCode: 201);
Stopwatch stopwatch = new Stopwatch();
stopwatch.start();
HttpClientResponse response = request(path: "");
stopwatch.stop();
expect(stopwatch.elapsed.inMilliseconds, greaterThanOrEqualTo(2000));
expect(response.statusCode, 201);
验证请求是否正确
您可以检查应用程序是否发送了正确的请求。可以通过获取服务器上的请求来实现,请求队列遵循后进先出(LIFO)原则。
server.enqueue(body: "a");
server.enqueue(body: "b");
server.enqueue(body: "c");
request(path: "first");
request(path: "second");
request(path: "third");
// takeRequest 是 FIFO
// 应该将 takeRequest() 赋值给一个变量,以便验证多个属性
expect(server.takeRequest().headers['x-header'], "nosniff");
expect(server.takeRequest().method, "GET");
expect(server.takeRequest().uri.path, "/third");
使用调度器进行细粒度路由
如果您需要比队列更多的控制,可以设置调度器并设置逻辑。
var dispatcher = (HttpRequest request) {
if (request.uri.path == "/users") {
return new MockResponse()
..httpCode = 200
..body = "working";
} else if (request.uri.path == "/users/1") {
return new MockResponse()..httpCode = 201;
}
return new MockResponse()..httpCode = 404;
};
server.dispatcher = dispatcher;
HttpClientResponse response = request(path: "unknown");
expect(response.statusCode, 404);
response = request(path: "users");
expect(response.statusCode, 200);
expect(read(response), "working");
response = request(path: "users/1");
expect(response.statusCode, 201);
使用 TLS
您可以通过传递 certificate
参数在创建 MockWebServer
实例时启用 TLS。
var chainRes =
new Resource('package:mock_web_server/certificates/server_chain.pem');
List<int> chain = await chainRes.readAsBytes();
var keyRes =
new Resource('package:mock_web_server/certificates/server_key.pem');
List<int> key = await keyRes.readAsBytes();
Certificate certificate = new Certificate()
..password = "dartdart"
..key = key
..chain = chain;
MockWebServer _server =
new MockWebServer(certificate: certificate);
如果客户端验证证书,则需要使用适当的 SecurityContext
,例如使用包含的 trusted_certs.pem
。
var certRes =
new Resource('package:mock_web_server/certificates/trusted_certs.pem');
List<int> cert = await certRes.readAsBytes();
SecurityContext clientContext = new SecurityContext()
..setTrustedCertificatesBytes(cert);
var client = new HttpClient(context: clientContext);
使用 IPv6
如果您想使用 IPv6,可以在构造函数中传递 addressType: InternetAddressType.IP_V6
来让服务器使用它。
MockWebServer _server =
new MockWebServer(port: 8030, addressType: InternetAddressType.IP_V6);
设置默认响应
在某些情况下,如果没有队列中的内容且没有调度器,您可能希望服务器返回一个默认响应(如 404
)而不是抛出异常。
_server.defaultResponse = MockResponse()..httpCode = 404;
停止服务器
在测试的 tearDown
阶段,应该停止服务器。使用 server.shutdown()
即可。
tearDown(() {
_server.shutdown();
});
完整示例代码
/*
* Copyright (C) 2017 Miguel Castiblanco
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import 'package:mock_web_server/mock_web_server.dart';
import 'package:test/test.dart';
import 'dart:io';
import 'dart:async';
import 'dart:convert';
import 'package:resource/resource.dart' show Resource;
MockWebServer _server;
void main() {
setUp(() {
_server = new MockWebServer();
_server.start();
});
tearDown(() {
_server.shutdown();
});
test("Set response code", () async {
_server.enqueue(httpCode: 401);
HttpClientResponse response = await _get("");
expect(response.statusCode, 401);
});
test("Set body", () async {
_server.enqueue(body: "something");
HttpClientResponse response = await _get("");
expect(await _read(response), "something");
});
test("Set headers", () async {
Map<String, String> headers = new Map();
headers["X-Server"] = "MockDart";
_server.enqueue(body: "Created", httpCode: 201, headers: headers);
HttpClientResponse response = await _get("");
expect(response.statusCode, 201);
expect(response.headers.value("X-Server"), "MockDart");
expect(await _read(response), "Created");
});
test("Set body and response code", () async {
_server.enqueue(body: "Created", httpCode: 201);
HttpClientResponse response = await _get("");
expect(response.statusCode, 201);
expect(await _read(response), "Created");
});
test("Set body, response code, and headers", () async {
Map<String, String> headers = new Map();
headers["X-Server"] = "MockDart";
_server.enqueue(body: "Created", httpCode: 201, headers: headers);
HttpClientResponse response = await _get("");
expect(response.statusCode, 201);
expect(response.headers.value("X-Server"), "MockDart");
expect(await _read(response), "Created");
});
test("Queue", () async {
_server.enqueue(body: "hello");
_server.enqueue(body: "world");
HttpClientResponse response = await _get("");
expect(await _read(response), "hello");
response = await _get("");
expect(await _read(response), "world");
});
test("Take requests & request count", () async {
_server.enqueue(body: "a");
_server.enqueue(body: "b");
_server.enqueue(body: "c");
await _get("first");
await _get("second");
await _get("third");
expect(_server.takeRequest().uri.path, "/first");
expect(_server.takeRequest().uri.path, "/second");
expect(_server.takeRequest().uri.path, "/third");
expect(_server.requestCount, 3);
});
test("Request count", () async {
_server.enqueue(httpCode: HttpStatus.unauthorized);
await _get("first");
expect(_server.takeRequest().uri.path, "/first");
expect(_server.requestCount, 1);
});
test("Dispatcher", () async {
var dispatcher = (HttpRequest request) async {
if (request.uri.path == "/users") {
return new MockResponse()
..httpCode = 200
..body = "working";
} else if (request.uri.path == "/users/1") {
return new MockResponse()..httpCode = 201;
} else if (request.uri.path == "/delay") {
return new MockResponse()
..httpCode = 200
..delay = new Duration(milliseconds: 1500);
}
return new MockResponse()..httpCode = 404;
};
_server.dispatcher = dispatcher;
HttpClientResponse response = await _get("unknown");
expect(response.statusCode, 404);
response = await _get("users");
expect(response.statusCode, 200);
expect(await _read(response), "working");
response = await _get("users/1");
expect(response.statusCode, 201);
Stopwatch stopwatch = new Stopwatch()..start();
response = await _get("delay");
stopwatch.stop();
expect(stopwatch.elapsed.inMilliseconds,
greaterThanOrEqualTo(new Duration(milliseconds: 1500).inMilliseconds));
expect(response.statusCode, 200);
});
test("Enqueue MockResponse", () async {
Map<String, String> headers = new Map();
headers["X-Server"] = "MockDart";
var mockResponse = new MockResponse()
..httpCode = 201
..body = "Created"
..headers = headers;
_server.enqueueResponse(mockResponse);
HttpClientResponse response = await _get("");
expect(response.statusCode, 201);
expect(response.headers.value("X-Server"), "MockDart");
expect(await _read(response), "Created");
});
test("Delay", () async {
_server.enqueue(delay: new Duration(seconds: 2), httpCode: 201);
Stopwatch stopwatch = new Stopwatch()..start();
HttpClientResponse response = await _get("");
stopwatch.stop();
expect(stopwatch.elapsed.inMilliseconds,
greaterThanOrEqualTo(new Duration(seconds: 2).inMilliseconds));
expect(response.statusCode, 201);
});
test('Parallel delay', () async {
String body70 = "70 milliseconds";
String body40 = "40 milliseconds";
String body20 = "20 milliseconds";
_server.enqueue(delay: new Duration(milliseconds: 40), body: body40);
_server.enqueue(delay: new Duration(milliseconds: 70), body: body70);
_server.enqueue(delay: new Duration(milliseconds: 20), body: body20);
Completer completer = new Completer();
List<String> responses = new List();
_get("").then((res) async {
// 40 milliseconds
String result = await _read(res);
responses.add(result);
});
_get("").then((res) async {
// 70 milliseconds
String result = await _read(res);
responses.add(result);
// complete on the longer operation
completer.complete();
});
_get("").then((res) async {
// 20 milliseconds
String result = await _read(res);
responses.add(result);
});
await completer.future;
// validate that the responses happened in order 20, 40, 70
expect(responses[0], body20);
expect(responses[1], body40);
expect(responses[2], body70);
});
test("Request specific port IPv4", () async {
MockWebServer _server = new MockWebServer(port: 8029);
await _server.start();
RegExp url = new RegExp(r'(?:http[s]?:\/\/(?:127\.0\.0\.1):8029\/)');
RegExp host = new RegExp(r'(?:127\.0\.0\.1)');
expect(url.hasMatch(_server.url), true);
expect(host.hasMatch(_server.host), true);
expect(_server.port, 8029);
_server.shutdown();
});
test("Request specific port IPv6", () async {
MockWebServer _server =
new MockWebServer(port: 8030, addressType: InternetAddressType.IPv6);
await _server.start();
RegExp url = new RegExp(r'(?:http[s]?:\/\/(?:::1):8030\/)');
RegExp host = new RegExp(r'(?:::1)');
expect(url.hasMatch(_server.url), true);
expect(host.hasMatch(_server.host), true);
expect(_server.port, 8030);
_server.shutdown();
});
test("TLS info", () async {
var chainRes =
new Resource('package:mock_web_server/certificates/server_chain.pem');
List<int> chain = await chainRes.readAsBytes();
var keyRes =
new Resource('package:mock_web_server/certificates/server_key.pem');
List<int> key = await keyRes.readAsBytes();
Certificate certificate = new Certificate()
..password = "dartdart"
..key = key
..chain = chain;
MockWebServer _server =
new MockWebServer(port: 8029, certificate: certificate);
await _server.start();
RegExp url = new RegExp(r'(?:https:\/\/(?:127\.0\.0\.1):8029\/)');
RegExp host = new RegExp(r'(?:127\.0\.0\.1)');
expect(url.hasMatch(_server.url), true);
expect(host.hasMatch(_server.host), true);
expect(_server.port, 8029);
_server.shutdown();
});
test("TLS cert", () async {
String body = "S03E08 You Are Not Safe";
var chainRes =
new Resource('package:mock_web_server/certificates/server_chain.pem');
List<int> chain = await chainRes.readAsBytes();
var keyRes =
new Resource('package:mock_web_server/certificates/server_key.pem');
List<int> key = await keyRes.readAsBytes();
Certificate certificate = new Certificate()
..password = "dartdart"
..key = key
..chain = chain;
MockWebServer _server =
new MockWebServer(port: 8029, certificate: certificate);
await _server.start();
_server.enqueue(body: body);
var certRes =
new Resource('package:mock_web_server/certificates/trusted_certs.pem');
List<int> cert = await certRes.readAsBytes();
// Calling without the proper security context
var clientErr = new HttpClient();
expect(clientErr.getUrl(Uri.parse(_server.url)),
throwsA(new TypeMatcher<HandshakeException>()));
// Testing with security context
SecurityContext clientContext = new SecurityContext()
..setTrustedCertificatesBytes(cert);
var client = new HttpClient(context: clientContext);
var request = await client.getUrl(Uri.parse(_server.url));
String response = await _read(await request.close());
expect(response, body);
_server.shutdown();
});
test("Check take request", () async {
_server.enqueue();
HttpClient client = new HttpClient();
HttpClientRequest request =
await client.post(_server.host, _server.port, "test");
request.headers.add("x-header", "nosniff");
request.write("sample body");
await request.close();
StoredRequest storedRequest = _server.takeRequest();
expect(storedRequest.method, "POST");
expect(storedRequest.body, "sample body");
expect(storedRequest.uri.path, "/test");
expect(storedRequest.headers['x-header'], "nosniff");
});
test("default response", () async {
_server.defaultResponse = MockResponse()..httpCode = 404;
var response = await _get("");
expect(response.statusCode, 404);
});
}
_get(String path) async {
HttpClient client = new HttpClient();
HttpClientRequest request =
await client.get(_server.host, _server.port, path);
return await request.close();
}
Future<String> _read(HttpClientResponse response) async {
StringBuffer body = new StringBuffer();
Completer<String> completer = new Completer();
response.transform(utf8.decoder).listen((data) {
body.write(data);
}, onDone: () {
completer.complete(body.toString());
});
await completer.future;
return body.toString();
}
更多关于Flutter本地模拟服务器插件mock_web_server的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter本地模拟服务器插件mock_web_server的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
mock_web_server
是一个用于在 Flutter 中模拟 HTTP 服务器的插件。它通常用于在本地开发环境中模拟 API 请求,以便在没有实际后端服务器的情况下进行测试和开发。
安装 mock_web_server
首先,你需要在 pubspec.yaml
文件中添加 mock_web_server
依赖:
dev_dependencies:
mock_web_server: ^5.0.0
然后运行 flutter pub get
来安装依赖。
使用 mock_web_server
以下是一个简单的示例,展示如何使用 mock_web_server
来模拟一个 HTTP 服务器并处理请求。
import 'package:flutter_test/flutter_test.dart';
import 'package:mock_web_server/mock_web_server.dart';
void main() {
late MockWebServer server;
setUp(() {
server = MockWebServer();
});
tearDown(() async {
await server.shutdown();
});
test('Test mock web server', () async {
// 启动服务器
await server.start();
// 模拟一个 GET 请求的响应
server.enqueue(
httpCode: 200,
body: 'Hello, World!',
);
// 发送请求到模拟服务器
final response = await http.get(Uri.parse(server.url));
// 验证响应
expect(response.statusCode, 200);
expect(response.body, 'Hello, World!');
});
test('Test POST request', () async {
// 启动服务器
await server.start();
// 模拟一个 POST 请求的响应
server.enqueue(
httpCode: 201,
body: '{"message": "Created"}',
headers: {'Content-Type': 'application/json'},
);
// 发送 POST 请求到模拟服务器
final response = await http.post(
Uri.parse(server.url),
body: '{"data": "value"}',
headers: {'Content-Type': 'application/json'},
);
// 验证响应
expect(response.statusCode, 201);
expect(response.body, '{"message": "Created"}');
});
}
主要功能
-
启动和停止服务器:
server.start()
: 启动模拟服务器。server.shutdown()
: 停止模拟服务器。
-
模拟响应:
server.enqueue()
: 用于模拟服务器的响应。你可以指定 HTTP 状态码、响应体和响应头。
-
获取服务器 URL:
server.url
: 获取模拟服务器的 URL,用于发送请求。
-
验证请求:
server.takeRequest()
: 获取并验证发送到服务器的请求。你可以检查请求的方法、路径、头和体。
示例:验证请求
test('Verify request details', () async {
await server.start();
server.enqueue(httpCode: 200, body: 'OK');
await http.get(Uri.parse('${server.url}/test'));
final request = server.takeRequest();
expect(request.method, 'GET');
expect(request.uri.path, '/test');
});