6

I have been working on a new API wrapper and don't want to be calling the API every time my unit tests run. So as described here, I'm mocking it.

I initially thought there was something wrong with the way I'm mocking it, but it seems the problem is elsewhere.

What I'm trying to accomplish is very simple. When my unit test run, I would like to return a value as if I had gone out to get the information from the external API I'm integrating with.

I initialise my class with http.Client as an optional parameter, so I can pass it in when the unit tests run as such:

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()));
}

In my unit test I have created the following mock class:

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

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

And in my unit test to check the response I'm doing the following:

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))));
});

So, as you can see, I am trying to make it so calling send returns message401, which is just a JSON string.

This is not happening, since message401 is a string, and because my code tries to use it as a Future, I always get the error:

Top-level uncaught error: Class 'String' has no instance method 'then'.

I totally understand why I'm getting this error, but have no idea about how to go around it.

Any help appreciated.

Community
  • 1
  • 1
Marcos Placona
  • 21,468
  • 11
  • 68
  • 93

4 Answers4

13

The http package has a testing library with a MockClient already implemented for you.

Community
  • 1
  • 1
Sean Eagan
  • 1,130
  • 7
  • 9
2

Try

.alwaysReturn(new Future.value(message401));
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • Your example absolutely worked, but I have marked Sean's answer as the correct one, since it offers a "native" way of accomplishing this. thank you so much though! – Marcos Placona Jun 14 '14 at 22:28
2

There is the nock package for that:

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");
  });
}

It uses HttpOverrides, so you don't need to inject MockClient. Works both with dio and http packages.

  • 1
    Okay, but I have a problem with it. I'm trying to create several interceptors (because I have several requests) but only 1 works ... – Jeremy HOFFMANN Feb 09 '21 at 11:47
1

A minimal example from the included tests for MockClient in the http package you can get from pub.dev

Add the http package to your pubspec.yaml file...

dependencies:
  http: ^0.12.2

In your unit test dart file...

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'));
  });
}
Baker
  • 24,730
  • 11
  • 100
  • 106