Search code examples
fluttermockingmockitotddflutter-test

error when Add a stub for a method using Mockito's 'when' API


Have this abstract class to get data from remote data source

abstract class NumberTriviaRemoteDataSource {
  /// Calls the http://numberapi.com/{number} endpoint.
  ///
  /// Throws a [ServerException] for all error code
  Future<NumberTriviaModel> getConcreteNumberTrivia(int number);
}

and have this abstract class for repository in domain layer

abstract class NumberTriviaRepository {
  Future<Either<Failure, NumberTrivia>> getConcreteNumberTrivia(int number);
}

bellow I created a test to implement NumberTriviaRepository class

@GenerateMocks([NumberTriviaRemoteDataSource])
void main() {
  late final NumberTriviaRepositoryImpl repository;
  late final MockNumberTriviaRemoteDataSource mockRemoteDataSource;
  setUp(() {
    mockRemoteDataSource = MockNumberTriviaRemoteDataSource();
    repository = NumberTriviaRepositoryImpl(
      numberTriviaRemoteDataSource: mockRemoteDataSource,
    );
  });
  group('getConcreteNumberTrivia', () {
    //to make test manageable create variables
    const tNumber = 1;
    const tNumberTriviaModel =
        NumberTriviaModel(text: 'test trivia', number: tNumber);
    const NumberTrivia tNumberTriviaEntity = tNumberTriviaModel;

    group('when device is online', () {
      test(
          'should return remote data when call to remote data source is successful',
          () async {
        //arrange                                
        when(mockRemoteDataSource.getConcreteNumberTrivia(any))  
            .thenAnswer((_) async => tNumberTriviaModel);               ---> Line *
        //act
        final result = await repository.getConcreteNumberTrivia(tNumber);
        //assert
        verify(mockRemoteDataSource.getConcreteNumberTrivia(tNumber));
        expect(result, equals(const Right(tNumberTriviaEntity)));
      });
    });
  });

bellow I implemented the NumberTriviaRepository class

class NumberTriviaRepositoryImpl implements NumberTriviaRepository {
  NumberTriviaRemoteDataSource numberTriviaRemoteDataSource;
  NumberTriviaRepositoryImpl(
      {required this.numberTriviaRemoteDataSource});
  @override
  Future<Either<Failure, NumberTrivia>> getConcreteNumberTrivia(
      int number) async {
    return Right(
        await numberTriviaRemoteDataSource.getConcreteNumberTrivia(number));
  }

and when run the test I get a Mock.noSuchMethod error :

MissingStubError: 'getConcreteNumberTrivia'
No stub was found which matches the arguments of this method call:
getConcreteNumberTrivia(1)

with an explanation which is: "Add a stub for this method using Mockito's 'when' API, or generate the MockNumberTriviaRemoteDataSource mock with a MockSpec with 'returnNullOnMissingStub: true' (see https://pub.dev/documentation/mockito/latest/annotations/MockSpec-class.html)."

The problem is I created a stub method for getConcreteNumberTrivia at line * but got error and don't have any idea how to fix this. By the way, I'm learning Clean Architecture using TestDrivenDevelopment so any comment would be appriciated.


Solution

  • use setUpAll() to initiate mocks and use when in setUpAll section

    @GenerateMocks([NumberTriviaRemoteDataSource])
    @GenerateMocks([NumberTriviaLocalDataSource])
    @GenerateMocks([NetworkInfo])
    void main() {
      late final NumberTriviaRepositoryImpl repository;
      late final MockNumberTriviaRemoteDataSource mockRemoteDataSource;
      late final MockNumberTriviaLocalDataSource mockLocalDataSource;
      late final MockNetworkInfo mockNetworkInfo;
    
      const tNumber = 1;
      const tNumberTriviaModel =
          NumberTriviaModel(text: 'test trivia', number: tNumber);
      const NumberTrivia tNumberTriviaEntity = tNumberTriviaModel;
      setUpAll(() {
        mockRemoteDataSource = MockNumberTriviaRemoteDataSource();
        mockLocalDataSource = MockNumberTriviaLocalDataSource();
        mockNetworkInfo = MockNetworkInfo();
        repository = NumberTriviaRepositoryImpl(
          numberTriviaRemoteDataSource: mockRemoteDataSource,
          numberTriviaLocalDataSource: mockLocalDataSource,
          networkInfo: mockNetworkInfo,
        );
    
        when(mockRemoteDataSource.getConcreteNumberTrivia(any))
            .thenAnswer((_) async => tNumberTriviaModel);
      });
      group('getConcreteNumberTrivia', () {
        //to make test manageable create variables
    
        test('should check if the device is online', () {
          //arrange
          when(mockNetworkInfo.isConnected).thenAnswer((_) async => true);
          // act
          repository.getConcreteNumberTrivia(tNumber);
          // assert
          verify(mockNetworkInfo.isConnected);
        });
        group('when device is online', () {
          setUp(() {
            when(mockNetworkInfo.isConnected).thenAnswer((_) async => true);
          });
          test(
              'should return remote data when call to remote data source is successful',
              () async {
            //arrange
            when(mockRemoteDataSource.getConcreteNumberTrivia(any))
                .thenAnswer((_) async => tNumberTriviaModel);
    
            //act
            final result = await repository.getConcreteNumberTrivia(tNumber);
            //assert
            verify(mockRemoteDataSource.getConcreteNumberTrivia(tNumber));
            expect(result, equals(const Right(tNumberTriviaEntity)));
          });
      });
    }