4

I have a problem when testing my classes that make http requests. I want to Mock the client so that every time the client makes a request, I can answer with a Mocked response. At the moment my code looks like this:

  final fn = MockClientHandler;
  final client = MockClient(fn as Future<Response> Function(Request));

  when(client.get(url)).thenAnswer((realInvocation) async =>
    http.Response('{"userId": 1, "id": 2, "title": "mock"}', 200));

However, when I run the test I get the following exception: type '_FunctionType' is not a subtype of type '(Request) => Future<Response>' in type cast test/data_retrieval/sources/fitbit_test.dart 26:32 main

According to Flutter/Dart Mockito should be used like this:

 final client = MockClient();

  // Use Mockito to return a successful response when it calls the
  // provided http.Client.
  when(client.get(Uri.parse('https://jsonplaceholder.typicode.com/albums/1')))
      .thenAnswer((_) async => http.Response('{"userId": 1, "id": 2, "title":"mock"}', 200));

In the example, the client is mocked without parameters, but I guess this has been changed since the documentation of MockClient now also accepts a parameter. I have no idea why this exception occurs and nothing can be found on the internet, so I was wondering if someone here knows why this exception is happening.

  • I suspect that the problem is from `final fn = MockClientHandler;` and later you do `fn as Future Function(Request)`. What is `MockClientHandler`? It sounds like it's a `typedef` (which would make it a *type*) and not an instance of a `Function`, which would cause the cast to fail. – jamesdlin Jun 21 '21 at 09:23
  • But when I don't add the cast it gives a compilation error: `The argument type 'Type' can't be assigned to the parameter type 'Future Function(Request)'.` Could you help me with how the cast should look like then? – Merdan Durmus Jun 21 '21 at 09:39
  • You missed the point. `final fn = MockClientHandler;` is what's wrong. (It's ultimately a useless assignment. There isn't much that you can meaningful do with `fn` afterward.) Once again: what is `MockClientHandler`? It's unclear what you expect `fn` to be. – jamesdlin Jun 21 '21 at 09:58
  • 1
    Ah, `MockClient` and `MockClientHandler` are provided by `package:http`'s testing library. As I suspcted, you need to provide an actual function. – jamesdlin Jun 21 '21 at 10:32

2 Answers2

4

package:http's MockClient is a bit of a misnomer. It's not really a mock (and it's certainly not related to package:mockito's Mock); it's more like a stub (or arguably a fake): it has an implementation that is meant to simulate a normal http.Client object.

Note that Flutter's Mockito examples create a MockClient object using package:mockito, but this is unrelated to the MockClient class provided by package:http itself.

final fn = MockClientHandler;
final client = MockClient(fn as Future<Response> Function(Request));

package:http's MockClient expects an instance of a MockClientHandler as an argument, but you are attempting to pass the MockClientHandler type itself. You are expected to provide an actual function (again, because MockClient has an actual implementation).

In other words, if you want to use package:http's MockClient, then you should do:

Future<Response> requestHandler(Request request) async {
   return http.Response('{"userId": 1, "id": 2, "title": "mock"}', 200));
}

final client = MockClient(requestHandler);

Or if you want to use package:mockito, you need to follow https://flutter.dev/docs/cookbook/testing/unit/mocking exactly and generate a Mockito-based MockClient class.

jamesdlin
  • 81,374
  • 13
  • 159
  • 204
0

This is what the doc's fetch_album_test.dart will look like when they get around to updating it! (tests pass)...

import 'package:flutter_test/flutter_test.dart';
import 'package:http/http.dart' as http;
import 'package:http/testing.dart';
import 'package:mocking/main.dart';
import 'package:mockito/annotations.dart';

// Generate a MockClient using the Mockito package.
// Create new instances of this class in each test.
@GenerateMocks([http.Client])
void main() {
  group('fetchAlbum', () {
    test('returns an Album if the http call completes successfully', () async {
      final client = MockClient((_) async =>
          http.Response('{"userId": 1, "id": 2, "title": "mock"}', 200));

      expect(await fetchAlbum(client), isA<Album>());
    });

    test('throws an exception if the http call completes with an error', () {
      final client = MockClient((_) async => http.Response('Not Found', 404));

      expect(fetchAlbum(client), throwsException);
    });
  });
}
Paul Sumpner
  • 447
  • 7
  • 8