如何在Flutter集成测试中模拟HTTP请求?

13

我正在尝试使用Mockito进行测试,这是我的测试代码:

import 'package:http/http.dart' as http;
import 'package:utgard/globals.dart' as globals;
import 'package:flutter_driver/flutter_driver.dart';
import 'package:test/test.dart';
import 'package:mockito/mockito.dart';

class MockClient extends Mock implements http.Client {}

void main() {
  group('Login flow', () {

    final SerializableFinder loginContinuePasswordButton =
        find.byValueKey('login_continue_password_button');

    FlutterDriver driver;

    setUpAll(() async {
      driver = await FlutterDriver.connect();
    });

    tearDownAll(() async {
      if (driver != null) {
        //await driver.close();
      }
    });


    test('login with correct password', () async {
      final client = MockClient();

      when(client.post('http://wwww.google.com'))
          .thenAnswer((_) async => http.Response('{"title": "Test"}', 200));

      globals.httpClient = client;

      await driver.enterText('000000');
      await driver.tap(loginContinuePasswordButton);
    });
  });
}

这是我的HTTP请求代码:

Future<Map<String, dynamic>> post({
  RequestType requestType,
  Map<String, dynamic> body,
}) async {
  final http.Response response =
      await globals.httpClient.post('http://wwww.google.com');

  print(response);

  final Map<String, dynamic> finalResponse = buildResponse(response);

  _managerErrors(finalResponse);

  return finalResponse;
}

这里是全局变量:

library utgard.globals;

import 'package:http/http.dart' as http;

http.Client httpClient = http.Client();

然而,我仍然收到HTTP错误,这表明HTTP并没有被模拟替换。


我在下面提出了一个建议答案,但这有点靠运气,因为我没有 final SerializableFinder loginContinuePasswordButton = find.byValueKey('login_continue_password_button'); 的代码。有一小部分可能问题就出在那里。 - TruongSinh
嗨,Felipe,你能找到一种在Flutter集成测试中模拟依赖项的方法吗?使用这里提到的DataHandler怎么样? - Mayur Dhurpate
4个回答

4
我找到的解决方案是在test_driver/app.dart中定义模拟对象,然后调用runApp函数。
import 'package:flutter/widgets.dart';
import 'package:flutter_driver/driver_extension.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:utgard/business/config/globals.dart';
import 'package:utgard/main.dart' as app;

class MockClient extends Mock implements http.Client {}

void main() {
  enableFlutterDriverExtension();

  final MockClient client = MockClient();
  // make your mocks here
  httpClient = client;

  runApp(app.MyApp());
}

2

我们无法看到您正在测试的代码,但是很可能不会发出此请求:

client.post('http://wwww.google.com')

使用模拟JSON文件是一个良好的实践,您不希望每次这些模拟文件更改时都要更改请求。

我建议您使用Mocktail而不是Mockito。

这样,您可以使用any来模拟任何调用:

// Simulate any GET :
mockHttpClient.get(any()))

// Simulate any POST :
mockHttpClient.post(any()))

以下是完整的解决方案:
class MockClient extends Mock implements http.Client {}
class FakeUri extends Fake implements Uri {}

void main() {

  setUp(() {
    registerFallbackValue(FakeUri()); // Required by Mocktail
  });
  tearDown(() {});

  MockHttpClient mockHttpClient = MockHttpClient();
  group('Login flow', () {
    test('login with correct password', () async {
      when(() => mockHttpClient.get(any())).thenAnswer(((_) async {
        return Response(mockWeatherResponse, 200);
      }));
    
    // Call the functions you need to test here

    }
  }
}

2

Instead of

      when(client.post('http://wwww.google.com'))
          .thenAnswer((_) async => http.Response('{"title": "Test"}', 200));

最初的回答:尝试使用任何代码,然后稍后进行断言。
        when(
          mockHttpClient.send(any),
        ).thenAnswer((_) async => http.Response('{"title": "Test"}', 200));
// ...
        final String capt = verify(client.send(captureAny)).captured;
        expect(capt, 'http://wwww.google.com');

有一小部分可能性是你模拟的参数不完全符合要求,所以使用any更加安全。


-1
在Flutter的集成测试中,没有办法模拟服务(包括HTTP调用)。我再次强调:这是不可能的。
(如果你正在编写小部件测试或单元测试,模拟是直接的。你可以编写自己的模拟对象,或者使用mocktail或mockito。)
对于本答案而言,如果你使用了`IntegrationTestWidgetsFlutterBinding.ensureInitialized()`,那么你就处于一个集成测试中——也就是说,如果测试正在模拟器或物理设备上运行。在这种情况下,应用代码和测试代码运行在不同的隔离环境中,因此你在测试代码中所做的任何操作都不会影响到应用代码的执行,反之亦然。它们彼此之间是相互隔离的。
回到使用flutter_driver的日子里,您可以使用DataHandler在测试和应用程序之间传递消息,如本文所述。现在flutter_driver已经过时了,并且integration_test包没有相应的功能;请参阅此GitHub问题,目前该问题仍然未解决。
Flutter团队对集成测试的概念是,它们必须完全模拟最终用户使用应用程序的情况。因此,不允许进行模拟。在小部件/单元测试中,所有HTTP请求都被存根(返回HTTP 400),但在集成测试中它们正常工作。
如果您需要在集成测试中实现可重复性,您将不得不在Flutter之外的环境层面解决这个问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接