Search code examples
flutterunit-testingmockitodio

Missing stub error on Mockito in Flutter. Trying to use post of Dio


I’m trying to perform a post request using Dio and test it with Mockito. But I don’t know why this error is happening, someone knows what is this?

Error on the tests:

User Profile Image should verify if post method is successfully called [E]          
  MissingStubError: 'post'
  No stub was found which matches the arguments of this method call:
  post('/CadastroUsuario/test@example.com/salvarFotoPerfil', {data: Instance of 'FormData', queryParameters: null, options: null, cancelToken: null, onSendProgress: null, onReceiveProgress: null})
  
  Add a stub for this method using Mockito's 'when' API, or generate the mock for MockDio with 'returnNullOnMissingStub: true'.
  package:mockito/src/mock.dart 190:7                                 Mock._noSuchMethod
  package:mockito/src/mock.dart 184:45                                Mock.noSuchMethod
  test/data/c_user_registration_data_source_test.mocks.dart 147:14    MockDio.post
  package:app_test/data/c_user_registration_data_source.dart 70:33  CUserRegistrationDataSource.setUserProfileImage

My test code:

group('User Profile Image', () {
    const email = 'test@example.com';

    var imagePath = File('test/assets/images/profile_image.png').path;
    var expectedUrl = uploadUserProfilePhotoPath.replaceFirst('{email}', email);

    test('should verify if post method is successfully called', () async {
      var formData = FormData.fromMap({
        'profileImage': await MultipartFile.fromFile(imagePath),
      });

      when(dio.post(
        expectedUrl,
        data: formData,
      )).thenAnswer(
        (_) async => Response(
          data: null,
          statusCode: 200,
          requestOptions: CommomMocks.getRequestOptions(expectedUrl),
        ),
      );

      await dataSource.setUserProfileImage(
        email: email,
        imagePath: imagePath,
      );

      verify(dio.post(expectedUrl, data: formData)).called(1);
    });
  });

Implementation:

@override
  Future<void> setUserProfileImage({
    required String email,
    required String imagePath,
  }) async {
    final http = _httpClientApp.instance();

    var apiUrl = uploadUserProfilePhotoPath.replaceFirst('{email}', email);
    var formData = FormData.fromMap({
      'profileImage': await MultipartFile.fromFile(imagePath),
    });

    final response = await http.post(apiUrl, data: formData);
    return _handleResponse(response);
  }

It shows that method post from the mocked Dio is missing. I already run the pub command to generate the mocks, and I make the stub before calling the implementation method. Is there something I'm missing?

Thanks in advance.


Solution

  • I think the problem was in the “FormData” instance in the prod function:

    var formData = FormData.fromMap({
        'profileImage': await MultipartFile.fromFile(imagePath),
    });
    

    Every time the test runs, a new instance is created and the stub is not the same.

    So the solution I found, is to use a type matcher to check if any parameter of type 'FormData' is called with the function.

     test('should verify if post method is successful called', () async {
        httpStubs.setUpMockHttpClientAppSuccess(expectedUrl, formData);
    
        await dataSource.setUserProfileImage(
          email: email,
          imagePath: imagePath,
        );
    
        verify(
          () => httpClientApp.post(
            expectedUrl,
            body: isA<FormData>(),
          ),
        ).called(1);
      });
    

    The stub function:

    void setUpMockHttpClientAppSuccess(String url, dynamic payload) {
    when(
      () => httpClientApp.post(
        any(),
        body: any(named: 'body'),
      ),
    ).thenAnswer(
      (_) async => Response(
        data: null,
        statusCode: 200,
        requestOptions: CommomMocks.getRequestOptions(url),
      ),
    );
    

    }

    I'm not sure if the solution is the best or the best practice, therefore this resolves my test problem. If someone knows a better solution, please tell me.

    Thanks to all!! :)