unit-testingdarttestingmockingstubbing

Mock method of class under test in Dart


I have a helper class (in Dart) like below:

class AppInfoHelper {

  Future<String> getAppName() async {
    return getPackageInfo().then((info) => info.appName);
  }

  Future<String> getAppVersion() async {
    return getPackageInfo().then((info) => info.version);
  }

  Future<String> getBuildNumber() async {
    return getPackageInfo().then((info) => info.buildNumber);
  }
   Future<PackageInfo> getPackageInfo() async => PackageInfo.fromPlatform();
 
}

The responsibility of this class is providing app info. I'm using PackageInfo as one of the library. And for some reasons, I won't supply PackageInfo as constructor parameter.

How can I write proper unit test for this class? I'm not sure how to do stubbing since we getPackageInfo() method is coming from class under test itself.

Note: I'm using mockito (https://pub.dev/packages/mockito) and some reference mentioned about doing something like spy, but I don't think it's available from Dart/Flutter mockito.


Solution

  • First, if you won't supply an instance of PackageInfo to your class then you'll have to create your own MockAppInfoHelper that will let you use a fake PackageInfo class.

    Here's an example of implementation:

    class MockAppInfoHelper extends AppInfoHelper {
      MockAppInfoHelper(this._packageInfo);
    
      final PackageInfo _packageInfo;
    
      @override
      Future<PackageInfo> getPackageInfo() async => _packageInfo;
    }
    

    And to mock a fake PackageInfo class you'll have to generate some mocks by adding the GenerateMocks annotation and running build_runner:

    @GenerateMocks([PackageInfo])
    import 'app_info_helper_test.mocks.dart'; // note that the name might change if your base file is not named app_info_helper_test
    

    Now you can start to write your test method, here's an example of a test for getAppName:

    void main() {
      group('AppInfoHelper', () {
        late MockPackageInfo mockPackageInfo;
        late MockAppInfoHelper mockAppInfoHelper;
    
        /// setUp is called before each test, it'll ensure that the mock is reset.
        setUp(() {
          mockPackageInfo = MockPackageInfo();
          mockAppInfoHelper = MockAppInfoHelper(mockPackageInfo);
        });
    
        test('test getAppName', () async {
          const testAppName = 'testAppName';
    
          // First arrange the mock to return the expected value.
          when(mockPackageInfo.appName).thenReturn(testAppName);
    
          // Then call the method you want to test.
          final result = await mockAppInfoHelper.getAppName();
    
          // Finally verify that the mock was called as expected.
          verify(mockPackageInfo.appName);
          expect(result, testAppName);
        });
      });
    }