首页 > 解决方案 > 在 Dart 中测试异常

问题描述

当围绕会抛出异常的代码编写测试时,Dart/Mockito(或其他任何东西)如何避免抛出真正的异常?例如,这些测试应该通过并检测到抛出的异常——但 Dart 在第一次测试中抛出了一个真正的异常,所以只有“它接收到一个 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.

标签: dartmockito-dart

解决方案


你的问题是你这样做:

expect(await fetchTodo(client, 1), throwsException);

expect()是一个普通函数,函数参数在函数被调用之前被评估。(Dart 是一种应用顺序语言。)因此,您必须调用之前等待fetchTodo完成(并且 before可以尝试匹配)。expect()expectthrowsException Matcher

正如throwsA文档所解释的(这也适用于throwsException Matcher),它必须与零参数函数或Future. 您不需要(也不应该)await调用fetchTodo.

此外,由于您正在调用异步函数,因此无法同步检查期望,因此您还需要使用expectLater而不是expect

await expectLater(fetchTodo(client, 1), throwsException);

推荐阅读