admin管理员组

文章数量:1221774

I'm trying to write a test for a function that configures a Dio instance to accept all SSL certificates, including those that are invalid or self-signed.

I have the following extension on Dio:

extension SSLExtension on Dio {
  void enableUnknownCertificates() {
    httpClientAdapter = IOHttpClientAdapter(
      createHttpClient: () {
        final HttpClient client = HttpClient(
          context: SecurityContext(withTrustedRoots: false),
        );
        client.badCertificateCallback = ((_, __, ___) => true);
        return client;
      },
    );
  }
}

In my test, I want to verify that:

  • Dio.httpClientAdapter is set to an instance of IOHttpClientAdapter.
  • The HttpClient is configured with SecurityContext(withTrustedRoots: false).
  • The badCertificateCallback is properly set and returns true when called.

However, I'm struggling to mock or stub the HttpClient inside IOHttpClientAdapter. When I try to test it, I encounter issues like:

  • The getter 'badCertificateCallback' isn't defined for the type 'HttpClient'.
  • Null check operator used on a null value.
  • No stub was found which matches the arguments of this method call.

Here’s the test I attempted (it's working):

class MockDioAdapter extends Mock implements IOHttpClientAdapter {}

class MockHttpClient extends Mock implements HttpClient {
  dynamic _badCertificateCallback;

  MockHttpClient({SecurityContext? context});

  @override
  set badCertificateCallback(dynamic callback) {
    print("badCertificateCallback assigned");
    _badCertificateCallback = callback;
  }

  bool callBadCertificateCallback(
      X509Certificate? cert, String host, int port) {
    return _badCertificateCallback != null
        ? _badCertificateCallback(cert, host, port)
        : false;
  }
}

Test Function:

@GenerateMocks([Dio])
main() {
  group('SSLExtension', () {
    late MockDio dio;
    late MockHttpClient mockHttpClient;

    setUp(() {
      dio = MockDio();
      mockHttpClient = MockHttpClient(context: SecurityContext(withTrustedRoots: false));
      mockHttpClient.badCertificateCallback = ((_, __, ___) => true);
    });

    test('enableUnknownCertificates should configure badCertificateCallback', () {
      // Arrange
      when(dio.httpClientAdapter).thenReturn(
        IOHttpClientAdapter(
          createHttpClient: () {
            return mockHttpClient;
          },
        ),
      );

      // Execute
      dio.enableUnknownCertificates();

      // Assert
      expect(dio.httpClientAdapter, isA<IOHttpClientAdapter>());
      expect(dio.httpClientAdapter, isNotNull);

      final adapter = dio.httpClientAdapter as IOHttpClientAdapter;
      final createHttpClient = adapter.createHttpClient;
      expect(createHttpClient, isNotNull);

      final client = createHttpClient!();
      expect(client, isNotNull);

      final result = mockHttpClient.callBadCertificateCallback(null, 'example', 443);
      expect(result, true);
    });

  });
}

However, I haven't been able to achieve full branch coverage for the client creation execution in my test. This part is missing:

final HttpClient client = HttpClient(
   context: SecurityContext(withTrustedRoots: false),
);
client.badCertificateCallback = ((_, __, ___) => true);

What is the best way to properly test the badCertificateCallback and SecurityContext setup in HttpClient? Should I mock HttpClient differently, or is there a better approach?

本文标签: