在 flutter/dart 中单元测试未来错误的正确方法是什么?
What is the proper way to unittest future errors in flutter/dart?
我正在编写的单元测试应该因异常而失败,我想匹配使用匹配器抛出的错误,但我收到了一个错误。
我目前做错了什么,这里的错误是什么?
如果我做错了,测试 returns 未来方法的错误场景的正确方法是什么?为什么会有异步间隙?
这是我要测试的方法:
Future<void> registerUser(String name, String email, String password) async {
auth.signUp(email, password, name).then((_) {
auth.getCurrentUser().then((user) async {
// Doing something
}).catchError((onError) {
throw onError;
});
这是我写的测试:
test('should fail to register if fetching user fails', () async {
MockAuth auth = MockAuth();
RegisterRepository repo = RegisterRepository(auth);
String password = 'password';
String email = 'email';
String name = 'name';
when(auth.signUp(email, password, name))
.thenAnswer((_) => Future.value());
when(auth.getCurrentUser()).thenThrow((_) => throw Error());
try {
await repo.registerUser(name, email, password);
fail('exception not thrown');
} catch (e) {}
verify(auth.signUp(email, password, name)).called(1);
verify(auth.getCurrentUser()).called(1);
verifyNoMoreInteractions(auth);
});
我收到这个错误:
package:mockito/src/mock.dart 403:7 PostExpectation.thenThrow.<fn>
package:mockito/src/mock.dart 130:45 Mock.noSuchMethod
package:dive/repository/register_repo.dart 25:12 RegisterRepository.registerUser.<fn>
===== asynchronous gap ===========================
dart:async Future.then
package:dive/repository/register_repo.dart 24:40 RegisterRepository.registerUser
test/repository/register_repo_test.dart 61:20 main.<fn>.<fn>
Closure: (dynamic) => Null
对于以后遇到此问题的任何其他人,
- 我把正在测试的方法中的async关键字去掉了,这里不需要async关键字。链接调用使其更具可读性:
Future<void> registerUser(String name, String email, String password) {
return auth
.signUp(email, password, name)
.then((_) => auth.getCurrentUser())
.then((user) {
// Doing something
}).catchError((onError) {
throw onError;
});
- 要测试未来的错误,请执行以下操作:
test('should fail to register if fetching user fails', () async {
MockAuth auth = MockAuth();
RegisterRepository repo = RegisterRepository(auth);
String password = 'password';
String email = 'email';
String name = 'name';
when(auth.signUp(email, password, name))
.thenAnswer((_) => Future.value());
when(auth.getCurrentUser()).thenAnswer((_) => Future.error('error'));
repo.registerUser(name, email, password).catchError((onError) {
expect(onError.toString(), 'error');
verify(auth.signUp(email, password, name)).called(1);
verify(auth.getCurrentUser()).called(1);
verifyNoMoreInteractions(auth);
});
});
测试现在通过了。
刚刚发现我们可以简单地做到这一点(查看 throwsA):
test("getNews SHOULD throw an exception WHEN api fails", () {
final ExceptionMock exception = ExceptionMock();
when(() => newsApi.getNews()).thenAnswer((realInvocation) => Future<List<JsonNews>>.error(exception));
expect(() => sut.getNews(), throwsA(exception));
});
请注意,我正在模拟异常。这样我就确信 api 确实被调用了。否则我不会捕获那个模拟的异常。使用具体对象时,无法确定是否返回值,因为依赖项实际上 returns 该对象或者它只是在 sun(被测系统)实现中硬编码。
我正在编写的单元测试应该因异常而失败,我想匹配使用匹配器抛出的错误,但我收到了一个错误。
我目前做错了什么,这里的错误是什么? 如果我做错了,测试 returns 未来方法的错误场景的正确方法是什么?为什么会有异步间隙?
这是我要测试的方法:
Future<void> registerUser(String name, String email, String password) async {
auth.signUp(email, password, name).then((_) {
auth.getCurrentUser().then((user) async {
// Doing something
}).catchError((onError) {
throw onError;
});
这是我写的测试:
test('should fail to register if fetching user fails', () async {
MockAuth auth = MockAuth();
RegisterRepository repo = RegisterRepository(auth);
String password = 'password';
String email = 'email';
String name = 'name';
when(auth.signUp(email, password, name))
.thenAnswer((_) => Future.value());
when(auth.getCurrentUser()).thenThrow((_) => throw Error());
try {
await repo.registerUser(name, email, password);
fail('exception not thrown');
} catch (e) {}
verify(auth.signUp(email, password, name)).called(1);
verify(auth.getCurrentUser()).called(1);
verifyNoMoreInteractions(auth);
});
我收到这个错误:
package:mockito/src/mock.dart 403:7 PostExpectation.thenThrow.<fn>
package:mockito/src/mock.dart 130:45 Mock.noSuchMethod
package:dive/repository/register_repo.dart 25:12 RegisterRepository.registerUser.<fn>
===== asynchronous gap ===========================
dart:async Future.then
package:dive/repository/register_repo.dart 24:40 RegisterRepository.registerUser
test/repository/register_repo_test.dart 61:20 main.<fn>.<fn>
Closure: (dynamic) => Null
对于以后遇到此问题的任何其他人,
- 我把正在测试的方法中的async关键字去掉了,这里不需要async关键字。链接调用使其更具可读性:
Future<void> registerUser(String name, String email, String password) {
return auth
.signUp(email, password, name)
.then((_) => auth.getCurrentUser())
.then((user) {
// Doing something
}).catchError((onError) {
throw onError;
});
- 要测试未来的错误,请执行以下操作:
test('should fail to register if fetching user fails', () async {
MockAuth auth = MockAuth();
RegisterRepository repo = RegisterRepository(auth);
String password = 'password';
String email = 'email';
String name = 'name';
when(auth.signUp(email, password, name))
.thenAnswer((_) => Future.value());
when(auth.getCurrentUser()).thenAnswer((_) => Future.error('error'));
repo.registerUser(name, email, password).catchError((onError) {
expect(onError.toString(), 'error');
verify(auth.signUp(email, password, name)).called(1);
verify(auth.getCurrentUser()).called(1);
verifyNoMoreInteractions(auth);
});
});
测试现在通过了。
刚刚发现我们可以简单地做到这一点(查看 throwsA):
test("getNews SHOULD throw an exception WHEN api fails", () {
final ExceptionMock exception = ExceptionMock();
when(() => newsApi.getNews()).thenAnswer((realInvocation) => Future<List<JsonNews>>.error(exception));
expect(() => sut.getNews(), throwsA(exception));
});
请注意,我正在模拟异常。这样我就确信 api 确实被调用了。否则我不会捕获那个模拟的异常。使用具体对象时,无法确定是否返回值,因为依赖项实际上 returns 该对象或者它只是在 sun(被测系统)实现中硬编码。