在 Dart 中测试异常
Testing exceptions in Dart
在围绕将抛出异常的代码编写测试时,Dart/Mockito(或其他任何东西)如何避免抛出真正的异常?
例如,这些测试应该同时通过并检测抛出的异常——但是 Dart 在第一个测试中抛出了一个真正的异常,所以只有 'It receives a Todo' 通过。
void main() {
test('It throws an exception', () async {
final client = MockClient();
when(client.get(Uri.parse('https://jsonplaceholder.typicode.com/todos/1'))).thenAnswer((_) async => http.Response('', 404));
expect(await fetchTodo(client, 1), throwsException);
});
test('It receives a Todo', () async {
final client = MockClient();
final jsonString = '''
{
"id": 1,
"userId": 1,
"title": "test",
"completed": false
}
''';
when(client.get(Uri.parse('https://jsonplaceholder.typicode.com/todos/2'))).thenAnswer((_) async => http.Response(jsonString, 200));
expect(await fetchTodo(client, 2), isA<Todo>());
});
}
和模拟的 get 方法(基于 mockito 生成的代码——我在测试文件中使用 @GenerateMocks([http.Client])
时得到相同的结果。
class MockClient extends Mock implements http.Client {
Future<http.Response> get(Uri url, {Map<String, String>? headers}) {
return super.noSuchMethod(Invocation.method(#get, [url], {#headers: headers}), returnValue: Future.value(http.Response('', 200))) as Future<http.Response>;
}
}
class Todo {
int id;
int userId;
String title;
bool completed;
Todo(this.id, this.userId, this.title, this.completed);
}
Future<Todo> fetchTodo(http.Client client, int id) async {
final response = await client.get(Uri.parse('https://jsonplaceholder.typicode.com/todos/$id'));
if(response.statusCode == 200) {
return Todo(1, 1, 'Test', true);
}else {
throw Exception('Failed to fetch resource');
}
}
测试运行报告:
00:00 +0: It throws an exception
00:00 +0 -1: It throws an exception [E]
Exception: Failed to fetch resource
test/test.dart 49:5 fetchTodo
00:00 +0 -1: It receives a Todo
00:00 +1 -1: Some tests failed.
你的问题是你做了:
expect(await fetchTodo(client, 1), throwsException);
expect()
是一个普通函数,函数参数在函数被调用之前计算。 (Dart 是一种 applicative-order 语言。)因此,您必须等待 fetchTodo
完成 ,然后 调用 expect()
(在 expect
之前可以尝试匹配 throwsException
Matcher
).
如 throwsA
文档所述(这也适用于 throwsException
Matcher
),它必须与零参数函数或 Future
.您不需要(也不应该)await
调用 fetchTodo
.
此外,由于您正在调用异步函数,因此无法同步检查期望值,因此您还需要使用 expectLater
而不是 expect
:
await expectLater(fetchTodo(client, 1), throwsException);
在围绕将抛出异常的代码编写测试时,Dart/Mockito(或其他任何东西)如何避免抛出真正的异常? 例如,这些测试应该同时通过并检测抛出的异常——但是 Dart 在第一个测试中抛出了一个真正的异常,所以只有 'It receives a Todo' 通过。
void main() {
test('It throws an exception', () async {
final client = MockClient();
when(client.get(Uri.parse('https://jsonplaceholder.typicode.com/todos/1'))).thenAnswer((_) async => http.Response('', 404));
expect(await fetchTodo(client, 1), throwsException);
});
test('It receives a Todo', () async {
final client = MockClient();
final jsonString = '''
{
"id": 1,
"userId": 1,
"title": "test",
"completed": false
}
''';
when(client.get(Uri.parse('https://jsonplaceholder.typicode.com/todos/2'))).thenAnswer((_) async => http.Response(jsonString, 200));
expect(await fetchTodo(client, 2), isA<Todo>());
});
}
和模拟的 get 方法(基于 mockito 生成的代码——我在测试文件中使用 @GenerateMocks([http.Client])
时得到相同的结果。
class MockClient extends Mock implements http.Client {
Future<http.Response> get(Uri url, {Map<String, String>? headers}) {
return super.noSuchMethod(Invocation.method(#get, [url], {#headers: headers}), returnValue: Future.value(http.Response('', 200))) as Future<http.Response>;
}
}
class Todo {
int id;
int userId;
String title;
bool completed;
Todo(this.id, this.userId, this.title, this.completed);
}
Future<Todo> fetchTodo(http.Client client, int id) async {
final response = await client.get(Uri.parse('https://jsonplaceholder.typicode.com/todos/$id'));
if(response.statusCode == 200) {
return Todo(1, 1, 'Test', true);
}else {
throw Exception('Failed to fetch resource');
}
}
测试运行报告:
00:00 +0: It throws an exception
00:00 +0 -1: It throws an exception [E]
Exception: Failed to fetch resource
test/test.dart 49:5 fetchTodo
00:00 +0 -1: It receives a Todo
00:00 +1 -1: Some tests failed.
你的问题是你做了:
expect(await fetchTodo(client, 1), throwsException);
expect()
是一个普通函数,函数参数在函数被调用之前计算。 (Dart 是一种 applicative-order 语言。)因此,您必须等待 fetchTodo
完成 ,然后 调用 expect()
(在 expect
之前可以尝试匹配 throwsException
Matcher
).
如 throwsA
文档所述(这也适用于 throwsException
Matcher
),它必须与零参数函数或 Future
.您不需要(也不应该)await
调用 fetchTodo
.
此外,由于您正在调用异步函数,因此无法同步检查期望值,因此您还需要使用 expectLater
而不是 expect
:
await expectLater(fetchTodo(client, 1), throwsException);