Home > database >  Unit Test (MultipartRequest in Flutter)
Unit Test (MultipartRequest in Flutter)

Time:01-04

This is a method I was trying to write a test for. I just want to write a very simple test that would just verify that send() was invoked. But the main trouble maker is the instance MultipartRequest. As you can see, I am directly using the MultipartRequest instance, not as a dependency. So, I don't think I am supposed to stub it. I can easily stub for getCachedToken() method as an instance of TokenValueLocalDataSource was passed in as a dependency. What do you think I should do to get my desired test result?

Future<Unit> addPost({
    File? imageFile,
    String? bodyText,
  }) async {
    if (imageFile != null || bodyText != null) {
      final tokenModel = await tokenValueLocalDataSource.getCachedToken();
      final stringUrl = EnvValues.addPostUrl;

      final request = http.MultipartRequest(
        'POST',
        Uri.parse(stringUrl),
      )..headers.addAll(
          {
            'Authorization': 'Token ${tokenModel.token}',
          },
        );

      if (bodyText != null) {
        request.fields['body'] = bodyText;
      }
      if (imageFile != null) {
        request.files.add(
          await http.MultipartFile.fromPath('image', imageFile.path),
        );
      }
      final response = await request.send(); // This needs to verified.
      if (response.statusCode == 200) {
        return unit;
      } else {
        throw ServerException();
      }
    } else {
      throw InvalidRequestException(
        "Both imageFile and bodyText can't be null",
      );
    }
  }

CodePudding user response:

Assuming you are using the http package, here is my answer:

Since the method itself instantiates the MultipartRequest and calls a method from it, it makes the code difficult/impossible to test. So it is easier to use the Client class. That is an abstract class that does the same thing and can be mocked. You can see the below example after a few refactoring:

class ApiClass {
  final http.Client _client;

  const ApiClass(this._client);

  Future<Unit> addPost({File? imageFile, String? bodyText}) async {
    if (imageFile != null || bodyText != null) {
      final tokenModel = await tokenValueLocalDataSource.getCachedToken();
      final stringUrl = EnvValues.addPostUrl;

      final request = http.MultipartRequest(
        'POST',
        Uri.parse(stringUrl),
      )..headers.addAll(
          {
            'Authorization': 'Token ${tokenModel.token}',
          },
        );

      if (bodyText != null) {
        request.fields['body'] = bodyText;
      }
      if (imageFile != null) {
        request.files.add(
          await http.MultipartFile.fromPath('image', imageFile.path),
        );
      }
      final response = await _client.send(request);
      if (response.statusCode == 200) {
        return unit;
      } else {
        throw ServerException();
      }
    } else {
      throw InvalidRequestException(
        "Both imageFile and bodyText can't be null",
      );
    }
  }
}

And the test will be something like this:

void main() {
  group('ApiClass', () {
    late ApiClass apiClass;
    late http.Client client;

    setUp(() {
      client = _MockClient();
      apiClass = ApiClass(client);
      registerFallbackValue(_FakeMultipartRequest());
    });

    test('your test description', () async {
      final response = _MockStreamedResponse();
      when(() => response.statusCode).thenReturn(200);
      when(
        () => client.send(any(
            that: isA<http.MultipartRequest>()
                .having((p0) => p0.files.length, 'has all files', 1))),
      ).thenAnswer((_) async => response);

      final result = await apiClass.addPost(bodyText: 'some text');
      expect(result, equals(unit));
    });
  });
}

class _MockStreamedResponse extends Mock implements StreamedResponse {}

class _FakeMultipartRequest extends Fake implements MultipartRequest {}

class _MockClient extends Mock implements Client {}
  • Related