Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

type 'Null' is not a subtype of type 'Future<Response>' when testing mocked http client with Mocktail

I have written a test for a simple HTTP get using Mocktail to mock the HTTP client. When I call the get method in the test I receive "type 'Null' is not a subtype of type 'Future'".

Anyone any idea why this might be?

Here is the test:

class MockHttpClient extends Mock implements http.Client {}

void main() {
  late IdRemoteDataSourceImpl dataSource;
  late MockHttpClient mockHttpClient;

  setUp(
    () {
      mockHttpClient = MockHttpClient();
      dataSource = IdRemoteDataSourceImpl(client: mockHttpClient);
    },
  );

  group('Get id', () {
    test(
      'when response code is 200',
      () async {
        final url = Uri.parse('https://api.test.com/');
        const tUsername = 'username';
        final accountJson = json.decode(
          fixture('account.json'),
          // ignore: avoid_as
        ) as Map<String, dynamic>;
        final tIdModel = IdModel.fromJson(accountJson);
        // arrange
        when(() => mockHttpClient.get(url))
            .thenAnswer((_) async => http.Response(
                  fixture('account.json'),
                  200,
                ));
        // act
        final testResult = await dataSource.getId(tUsername);
        // assert
        // expect(testResult, tIdModel);
      },
    );
  });
}

The error occurs when the following line runs:

final testResult = await dataSource.getId(tUsername);

Code being tested:

import 'dart:convert';

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

class IdModel {
  IdModel({required this.id});

  final String id;

  factory IdModel.fromJson(Map<String, dynamic> json) {
    return IdModel(id: json['id'].toString());
  }
}

abstract class IdRemoteDataSource {
  Future<IdModel> getId(String username);
}

class IdRemoteDataSourceImpl implements IdRemoteDataSource {
  IdRemoteDataSourceImpl({required this.client});

  final http.Client client;

  @override
  Future<IdModel> getId(String username) async {
    final url = Uri.parse('https://api.test.com/query?username=$username');
    final response = await client.get(url);
    // ignore: avoid_as
    final responseJson = json.decode(response.body) as Map<String, dynamic>;
    return IdModel.fromJson(responseJson);
  }
}
like image 688
Luke Avatar asked Jun 09 '21 06:06

Luke


1 Answers

Error type 'Null' is not a subtype of type 'Future'... occurs when you call method that has not been implemented for mock object or there are different parameters passed to it.

In your code you passed different url parameter to get(...) method. Http client mock waiting for 'https://api.test.com/' but actually 'https://api.test.com/query?username=$username' has been passed.

You have two options to solve it.

  1. Pass the same url to mocked method from when(...) that will be passed during test:
const tUsername = 'username';
final url = Uri.parse('https://api.test.com/query?username=$tUsername');
...
// arrange
when(() => mockHttpClient.get(url))
  .thenAnswer((_) async => http.Response(
      fixture('account.json'),
      200,
  ),
);

  1. Use any matcher (if you don't care which parameter passed):
registerFallbackValue(Uri.parse(''));
...
when(() => mockHttpClient.get(any()))
  .thenAnswer((_) async => http.Response(
      fixture('account.json'),
      200,
  ),
);

like image 167
Mol0ko Avatar answered Oct 18 '22 22:10

Mol0ko