Dart单元测试:等待类构造函数中异步函数执行结果的方法
解决异步初始化导致单元测试无法获取到mainFoo的问题
针对你的场景,有几种可行的方案来确保单元测试时能在loadMainFoo执行完成后再获取mainFoo的值:
方案一:暴露初始化完成的Future
在ViewModel中保存loadMainFoo的执行Future,对外提供等待初始化完成的方法,测试时先await这个方法再断言:
修改后的ViewModel代码:
class FooViewModel extends BaseViewModel { Foo? mainFoo; final Future<void> _initFuture; FooViewModel() : _initFuture = _loadMainFoo() { // 可选:这里可以处理初始化完成后的回调逻辑 } Future<void> _loadMainFoo() async { // 原Firestore加载逻辑,设置mainFoo mainFoo = await fetchFooFromFirestore(); } // 对外暴露等待初始化完成的方法 Future<void> waitForInitialization() => _initFuture; }
对应的测试代码:
test('确保mainFoo加载完成后不为null', () async { // ARRANGE sut = FooViewModel(); await sut.waitForInitialization(); // 等待异步加载完成 // ACT final result = sut.mainFoo; // ASSERT expect(result, isNotNull); });
方案二:使用异步工厂构造函数
利用Dart的异步工厂构造函数,确保返回ViewModel实例时mainFoo已经加载完成:
修改后的ViewModel代码:
class FooViewModel extends BaseViewModel { Foo? mainFoo; // 私有构造函数,禁止直接实例化 FooViewModel._(); // 异步工厂构造函数,返回完全初始化后的实例 static Future<FooViewModel> create() async { final viewModel = FooViewModel._(); await viewModel.loadMainFoo(); return viewModel; } Future<Foo> loadMainFoo() async { mainFoo = await fetchFooFromFirestore(); return mainFoo!; } }
对应的测试代码:
test('确保mainFoo加载完成后不为null', () async { // ARRANGE sut = await FooViewModel.create(); // 直接获取已初始化完成的实例 // ACT final result = sut.mainFoo; // ASSERT expect(result, isNotNull); });
方案三:直接await公开的loadMainFoo方法
如果loadMainFoo是公开方法(你的代码中已经是公开的),测试时可以直接调用并await该方法:
修改后的测试代码:
test('确保mainFoo加载完成后不为null', () async { // ARRANGE sut = FooViewModel(); await sut.loadMainFoo(); // 等待异步加载逻辑完成 // ACT final result = sut.mainFoo; // ASSERT expect(result, isNotNull); });
各方案适用场景
- 方案一:适合不想改变现有构造方式,希望保持原实例化逻辑的场景
- 方案二:适合要求实例必须完全初始化后才能使用的场景,避免未初始化状态的访问
- 方案三:最简单直接,适合
loadMainFoo方法本身需要对外暴露的场景
内容的提问来源于stack exchange,提问作者Charlie Page
相关产品推荐
相关产品推荐

