0

我第一次开始单元测试。我正在关注resoCoder的教程

这是我的测试代码,我在其中模拟我的 dbManager 类,但我无法模拟 DAO,因为它们是在 moor 中自动生成的,并且没有针对它们的 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 类类似的问题,其中我有一个用于UserPrefs 的getter。

UserPrefs get user => UserPrefs(_pref);

当我访问_prefManager.user.name测试时,它会引发相同的错误。我该如何解决呢?

4

1 回答 1

1

我相信您缺少一定程度的嘲笑,从而导致空异常。请记住,模拟类对所有内容都返回 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在模拟它之前为 null。所以这一行:

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

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


_prefManager.user.name我猜的问题是一样的。您需要模拟 User 类并为 _prefManager.user 提供一个when/returnMockUser 以提供另一个级别的when/returnfor _prefManager.user.name

这就像 Mockception ......你需要在你的模拟中更深入地了解。;)

于 2021-01-17T05:05:54.113 回答