Unit Testing your http.MultipartRequest network method

remejuan

Reme Le Hane

Posted on August 10, 2020

Unit Testing your http.MultipartRequest network method

Recently I had to implement image uploading which required form data that cannot be done with the standard http.Clientand instead needs http.MultipartRequest.

We have been doing a big push to implement a proper TDD approach in our products over at Wyzetalk, so one of the first steps was to get this new method tested, however, it was not as easy as it was with testing http.Client.

Unlike with http.Client one needs to provide an instance that includes the 2 required arguments, type, and Uri. With our product being a white label and the file upload being used from multiple widgets I needed to cater for the possibility of different Uri’s. Then also for testing, in order to mock the method, I would need to pass it into the class or function.

Here is a simplified version of my resulting implementation.

Future<String> fileUpload({
  @required http.MultipartRequest multipartRequest,
  @required http.MultipartFile fileData,
  @required Map<String, String> payload,
}) async {
  final request = await multipartRequest
    ..headers.addAll({
      ...deviceInfoHeaders,
      "Authorization": 'Bearer FAKE\_TOKEN',
    })
    ..fields.addAll(payload)
    ..files.add(fileData);

  final response = await request.send();
  final respStr = await response.stream.bytesToString();

  if (response.statusCode == 200) {
    return respStr;
  } else {
    throw ServerException();
  }
}
Enter fullscreen mode Exit fullscreen mode

I am passing in both the multiPartRequestas well as the fileData so that the implementation point can prepare the data based on the type, image vs doc.

The payloadData is just any extra info that gets provided by the uploading widget.

Mocking http.Client is very simple as you can see by the below snippet.

class MockClient extends Mock implements http.Client {}
Enter fullscreen mode Exit fullscreen mode

In order to get http.MultipartRequest there are a few extra things you may need to provide based on your specific use case.

class MockMultipartRequest extends Mock implements http.MultipartRequest {
  @override
  final Map<String, String> headers = {};

  @override
  final fields = <String, String>{};

  @override
  final files = <MultipartFile>[];
}
Enter fullscreen mode Exit fullscreen mode

In my case, as you can see in the functions code sample, I am providing headers, fields in the case of the payload, and files.

For that, within the mock class, I have also added the overrides for the 3 fields to ensure the logical default persists within the tests.

void main() {
  NetworkFileManager networkFileManager;
  MockDeviceInformationHelper mockDeviceInformationHelper;
  MockMultipartRequest mockMultipartRequest;

setUp(() {
  mockMultipartRequest = MockMultipartRequest();
  mockDeviceInformationHelper = MockDeviceInformationHelper();

networkFileManager = NetworkFileManager(
    deviceInformation: mockDeviceInformationHelper,
    headers: fixtureApiHeadersAuthed,
  );

when(mockDeviceInformationHelper.getDeviceInformation())
    .thenAnswer((\_) async => fixtureDeviceInfo);
  });

group('apiFileUpload', () {
    final mockPayload = Map<String, String>();
    final mockFile = http.MultipartFile.fromBytes(
      "file",
      [0],
      filename: 'file-name',
      contentType: http\_parser.MediaType(
        'image',
        'jpeg',
      ),
    );

test('should perform a File Upload request', () async {
      //arrange
      when(mockMultipartRequest.send()).thenAnswer(
        (\_) async => http.StreamedResponse(Stream.value([0]), 200),
      );
      //act
      await networkFileManager.fileUpload(
        payload: mockPayload,
        fileData: mockFile,
        multipartRequest: mockMultipartRequest,
      );
      //assert
      verify(mockMultipartRequest.send());
    });
  });
}
Enter fullscreen mode Exit fullscreen mode

Here is a snippet from the test for the above function, I am passing in a mockFile and mocking the request's response, which returns an http.StreamedResponse which takes a Steam.value and the 200 response code.

This setup allows us to verify that when the function is called, the network request should get made by verifying that the mockMultipartRequest.send() is in fact called.

I hope that you have found this post interesting or useful if you spot any mistakes or have any suggestions feel free to let me know.

Wish to carry on with the topic of Unit Testing, take a look at:

Also, follow our publication for upcoming updates on testing and other flutter things we figure out.

About Wyzetalk

Founded in South Africa and headquartered in The Netherlands, Wyzetalk is a leading global employee experience company that offers a mobile-first digital solution connecting large organisations with their dispersed, frontline workforce to improve communication, unleash innovation, and boost business performance. Since launching in 2012, the company has grown in revenue by more than 100% per annum. With a presence in 18 countries across five continents, today there are 650 000 employees making use of the Wyzetalk platform through clients in the Mining, Retail, FMCG, Manufacturing, Energy, Automotive and Shipping sectors.

Website: https://www.wyzetalk.com/

💖 💪 🙅 🚩
remejuan
Reme Le Hane

Posted on August 10, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related