【问题标题】:Mocking HTTP response with Dart使用 Dart 模拟 HTTP 响应
【发布时间】:2026-01-30 22:45:01
【问题描述】:

我一直在开发新的 API 包装器,不想在每次运行单元测试时都调用 API。所以正如here 所述,我在嘲笑它。

我最初认为我嘲笑它的方式有问题,但似乎问题出在其他地方。

我想要完成的事情非常简单。当我的单元测试运行时,我想返回一个值,就好像我已经出去从我正在集成的外部 API 获取信息一样。

我使用 http.Client 作为可选参数来初始化我的类,因此我可以在单元测试运行时将其传入:

SampleClass(String arg1, String arg2, [http.Client httpClient = null]) {
    this._arg1 = arg1;
    this._arg2 = arg2;
    _httpClient = (httpClient == null) ? http.Request : httpClient;
}

Future apiRequest(String resource, [Map<String, String> body]) {
    var url = buildBaseUrl(resource).toString();
    var request = new http.Request('POST', Uri.parse(url));
    request.bodyFields = body;
    return this._httpClient.send(request).then((response) => response.stream.bytesToString().then((value) => value.toString()));
}

在我的单元测试中,我创建了以下模拟类:

class HttpClientMock extends Mock implements http.Client {
  noSuchMethod(i) => super.noSuchMethod(i);
}

class HttpResponseMock extends Mock implements http.Response {
    noSuchMethod(i) => super.noSuchMethod(i);
}

在我的单元测试中检查响应我正在执行以下操作:

test("Send SMS errors with wrong account", () {
    var mockHttpClient = new HttpClientMock()
                             ..when(callsTo('send')).alwaysReturn(message401);
    var sample = new SampleClass(_arg1, _arg2, mockHttpClient);
    future = sample.apiRequest(...parameters here...).then((value) => value.toString());
    expect(future.then((value) => JSON.decode(value)), completion(equals(JSON.decode(message401))));
});

所以,如您所见,我正在尝试使其调用 send 返回 message401,这只是一个 JSON 字符串。

这不会发生,因为 message401 是一个字符串,并且因为我的代码试图将它用作 Future,所以我总是得到错误:

*未捕获错误:“字符串”类没有实例方法 '那么'。

我完全理解为什么会收到此错误,但不知道如何解决。

任何帮助表示赞赏。

【问题讨论】:

    标签: mocking dart dart-unittest dart-mock


    【解决方案1】:

    http 包有一个 testing library 和一个 MockClient 已经为您实现。

    【讨论】:

    • 你们能否发布一个使用 MockClient 的示例
    • @Frank 答案中的链接有一个例子
    【解决方案2】:

    试试

    .alwaysReturn(new Future.value(message401));
    

    【讨论】:

    • 您的示例绝对有效,但我已将肖恩的答案标记为正确答案,因为它提供了一种“本机”方式来完成此任务。非常感谢你!
    【解决方案3】:

    nock 包:

    import 'package:test/test.dart';
    import 'package:http/http.dart' as http;
    import 'package:nock/nock.dart';
    
    void main() {
      setUpAll(() {
        nock.init();
      });
    
      setUp(() {
        nock.cleanAll();
      });
    
      test("example", () async {
        final interceptor = nock("http://localhost/api").get("/users")
          ..reply(
            200,
            "result",
          );
    
        final response = await http.get("http://localhost/api/users");
    
        expect(interceptor.isDone, true);
        expect(response.statusCode, 200);
        expect(response.body, "result");
      });
    }
    

    它使用 HttpOverrides,所以你不需要注入 MockClient。适用于 diohttp 包。

    【讨论】:

    • 好的,但我有一个问题。我正在尝试创建几个拦截器(因为我有几个请求)但只有 1 个有效...
    【解决方案4】:

    您可以从 pub.dev 获得的 http 包中包含的 tests for MockClient 的最小示例

    添加httppackage to your pubspec.yaml file...

    dependencies:
      http: ^0.12.2
    

    在您的单元测试 dart 文件中...

    import 'dart:convert';
    
    import 'package:http/http.dart';
    import 'package:http/testing.dart';
    import 'package:test/test.dart';
    
    void main() {
    
      test('handles a request', () async {
        var client = MockClient((request) async {
          return Response(json.encode(request.bodyFields), 200, request: request);
        }
        );
    
        var response = await client.post('http://example.com/foo', body: {'field1': 'value1'});
    
        expect(response.body, contains('value1'));
      });
    }
    

    【讨论】: