flutter:单元测试 dao in moor

flutter: unit testing dao in moor

我第一次开始单元测试。我正在学习 resoCoder

的教程

这是我的测试代码,我在其中模拟我的 dbManager class 但我无法模拟 DAO,因为它们是在沼泽中自动生成的,并且没有 setter 方法。

class MockDbManager extends Mock implements DbManager{}

void main() {
  RecipeLocalDataSource dataSource;
  MockDbManager _dbManager;
  setUp(() {
    _dbManager = MockDbManager();
    dataSource = RecipeLocalDataSource(_dbManager);
  });

  group('Search Food Table', (){
    List<FoodTableData> getFoodTable(){
      var list = [];
      for(var i =1; i <=5 ; i++){
        list.add(FoodTableData(id: i, name: 'item $i'));
      }
      return list;
    }
    var searchQuery = 'query';

    test('Should return foodTableData when query is successful', (){
      //arrange
      when(_dbManager.foodTableDao.searchFoods(searchQuery)).thenAnswer((realInvocation) async => getFoodTable());
      //act
      var result = dataSource.searchFoodTable('test');
      //assert
      verify(_dbManager.foodTableDao.searchFoods(searchQuery));
      expect(getFoodTable(), result);
    });
  });
}

我收到以下错误

NoSuchMethodError: The method 'searchFoods' was called on null.
Receiver: null
Tried calling: searchFoods("query")

我理解错误但不知道如何解决。

此外,我在 preferenceManager class 中也遇到了类似的问题,其中我有一个 getter 用于 UserPrefs。

UserPrefs get user => UserPrefs(_pref);

当我访问 _prefManager.user.name 进行测试时,它抛出了同样的错误。我该如何解决?

我相信你错过了一定程度的模拟,导致空异常。请记住所有内容的模拟 class returns null。您必须为所有内容提供值。

你嘲笑了 DbManager,但没有嘲笑 DbManager里面 的 foodTableDao 字段。

// I don't have your classes, so these are just my guesses at matching their interface
abstract class TableDao {
  String searchFoods();
}

abstract class DbManager {
  TableDao foodTableDao;
}


class MockDbManager extends Mock implements DbManager {}

class MockTableDao extends Mock implements TableDao {}
// ↑ Define this mocked class as well

void main() {
  test('Mockito example', () {
    final dbManager = MockDbManager();

    final mockTableDao = MockTableDao();
    // ↑ instantiate this mocked class which will be a field value inside dbManager
    
    // Mock the field access for dbManager.foodTableDao
    when(dbManager.foodTableDao).thenReturn(mockTableDao);
    //  ↑ You're missing this ↑ level of stubbing, so add this

    when(mockTableDao.searchFoods()).thenAnswer((realInvocation) => 'Cucumber');
    // ↑ define the stubbing at the mockTableDao level, not dbManger.mockTableDao.searchFoods
    
    expect(dbManager.foodTableDao.searchFoods(), 'Cucumber');
    // You can use foodTableDao field here, getting the mocked value as expected
  });
}

在上面的示例中,您无法访问 dbManager.foodTableDao 来为 dbManager.foodTableDao.searchFoods() 设置模拟,因为 dbManager.foodTableDao 在您模拟它之前为空。所以这一行:

when(_dbManager.foodTableDao.searchFoods(searchQuery)).thenAnswer((realInvocation) async => getFoodTable());

抛出空异常,而不是下面的 expect 测试。 (从未达到预期测试。)


_prefManager.user.name 的问题与我猜测的相同。您需要模拟 User class 并为 _prefManager.user 提供 MockUser 的 when/return,以便为 _prefManager.user.name.

提供另一级别的 when/return

这就像 Mockception...您需要在您的模拟中更深一层。 ;)