我正在为我的StateNotifierProvider
编写单元测试,但在测试失败场景时,我遇到了一个奇怪的场景。由于某种原因,单元测试没有捕获初始状态。下面是提供程序实现:
final paymentBundleVM = StateNotifierProvider.autoDispose<PaymentBundleVM, PaymentBundleState>(
(ref) => PaymentBundleVM(ref.read(getPaymentBundleUseCase), ref.read(createPaymentBundleUseCase)),
dependencies: [getPaymentBundleUseCase, createPaymentBundleUseCase],
name: 'paymentBundleVM',
);
class PaymentBundleVM extends StateNotifier<PaymentBundleState> {
PaymentBundleVM(this._getBundleUseCase, this._createBundleUseCase) : super(PaymentBundleLoading()) {
unawaited(_loadDependencies());
}
final GetPaymentBundleUseCase _getBundleUseCase;
final CreatePaymentBundleUseCase _createBundleUseCase;
Future<void> _loadDependencies() async {
try {
final bundle = await _getBundleUseCase.run();
if (mounted) {
if (bundle != null)
state = PaymentBundleLoaded(bundle);
else
state = PaymentBundleDisabled();
}
} on BaseException catch (exception) {
if (mounted) state = PaymentBundleFailedLoadingState(exception);
}
}
}
abstract class PaymentBundleState extends Equatable {
@override
List<Object?> get props => [];
}
class PaymentBundleLoading extends PaymentBundleState {}
class PaymentBundleDisabled extends PaymentBundleState {}
class PaymentBundleLoaded extends PaymentBundleState {
PaymentBundleLoaded(this.bundle);
final PaymentBundle bundle;
@override
List<Object?> get props => [...super.props, bundle];
}
class PaymentBundleFailedLoadingState extends PaymentBundleState {
PaymentBundleFailedLoadingState(this.exception);
final BaseException exception;
@override
List<Object?> get props => [...super.props, exception];
}
字符串
下面是我的单元测试代码:
void main() {
final getPaymentBundleUseCaseMock = GetPaymentBundleUseCaseMock();
final createPaymentBundleUseCaseMock = CreatePaymentBundleUseCaseMock();
late ProviderContainer container;
final fakeBundle = PaymentBundleMock();
setUp(() {
container = ProviderContainer(
overrides: [
getPaymentBundleUseCase.overrideWith((ref) => getPaymentBundleUseCaseMock),
createPaymentBundleUseCase.overrideWith((ref) => createPaymentBundleUseCaseMock),
],
);
});
tearDown(() {
reset(getPaymentBundleUseCaseMock);
reset(createPaymentBundleUseCaseMock);
});
test('test A', () async {
when(getPaymentBundleUseCaseMock.run).thenAnswer((_) => Future.value(fakeBundle));
final listener = ListenerMock<PaymentBundleState>();
container.listen<PaymentBundleState>(paymentBundleVM, listener, fireImmediately: true);
await container.pump();
verifyInOrder(
[
() => listener(null, PaymentBundleLoading()),
() => listener(PaymentBundleLoading(), PaymentBundleLoaded(fakeBundle)),
],
);
verifyNoMoreInteractions(listener);
});
test('test B', () async {
when(getPaymentBundleUseCaseMock.run).thenAnswer((_) => Future.value());
final listener = ListenerMock<PaymentBundleState>();
container.listen<PaymentBundleState>(paymentBundleVM, listener, fireImmediately: true);
await container.pump();
verifyInOrder(
[
() => listener(null, PaymentBundleLoading()),
() => listener(PaymentBundleLoading(), PaymentBundleDisabled()),
],
);
verifyNoMoreInteractions(listener);
});
test('test C', () async {
final exception = HttpException.handshake();
when(getPaymentBundleUseCaseMock.run).thenThrow(exception);
final listener = ListenerMock<PaymentBundleState>();
container.listen<PaymentBundleState>(paymentBundleVM, listener, fireImmediately: true);
await container.pump();
verifyInOrder(
[
() => listener(null, PaymentBundleLoading()),
() => listener(PaymentBundleLoading(), PaymentBundleFailedLoadingState(exception)),
],
);
verifyNoMoreInteractions(listener);
});
}
型
让我感到奇怪的是,test A
和test B
都能按预期工作,但第三个却抛出了问题:
Matching call #0 not found. All calls: ListenerMock<PaymentBundleState>.call(
null,
PaymentBundleFailedLoadingState( [BaseException - handshakeHttp] null.
Server message: null
User message: null
statusCode: null
))
型
因此,基本上这个测试考虑的是StateNotifierProvider
的第一个状态是null
,之后它直接转换到PaymentBundleFailedLoadingState
。问题是,为什么PaymentBundleLoading
不在状态栈中考虑,因为它是提供程序示例化时发出的第一个状态?根据我的理解,预期的状态堆栈应该是null -> loading -> failedLoading
,而不是null -> failedLoading
1条答案
按热度按时间1zmg4dgp1#
根据@Randal Schwartz的建议,我重构了代码,用
AsyncNotifierProvider
替换了StateNotifierProvider
,这减少了大量的代码样板,提高了可测试性。我最终得到了以下提供程序和测试套件:供应商:
字符串
测试:
型