2

尝试使用 Mockito 测试我的 BLoC,BLoC 使用存储库类进行服务器调用,如果用户未通过身份验证,服务器调用函数应该抛出自定义异常。

但是当我试图存根存储库函数以抛出该自定义异常时,测试失败并出现以下错误:

sunapsis Authorization error (test error): test description

package:mockito/src/mock.dart 342:7                                     PostExpectation.thenThrow.<fn>
package:mockito/src/mock.dart 119:37                                    Mock.noSuchMethod
package:sunapsis/datasource/models/notifications_repository.dart 28:37  MockNotificationRepository.getNotificationList
package:sunapsis/blocs/notification_blocs/notification_bloc.dart 36:10  NotificationBloc.fetchNotifications
test/blocs/notification_blocs/notification_bloc_test.dart 53:48         main.<fn>.<fn>.<fn>
===== asynchronous gap ===========================
dart:async                                                              scheduleMicrotask
test/blocs/notification_blocs/notification_bloc_test.dart 53:7          main.<fn>.<fn>

这就是我的 BLoC 代码的样子:fetchNotifications函数调用存储库函数并处理响应和错误。有两个 catchError 块,一个处理 AuthorizationException 情况,另一个处理任何其他异常。以不同方式处理 AuthorizationException,因为它将用于设置应用程序的登录状态。

通知块.dart

import 'dart:async';

import 'package:logging/logging.dart';
import 'package:rxdart/rxdart.dart';
import 'package:sunapsis/datasource/dataobjects/notification.dart';
import 'package:sunapsis/datasource/models/notifications_repository.dart';
import 'package:sunapsis/utils/authorization_exception.dart';

class NotificationBloc {
  final NotificationsRepository _notificationsRepository;

  final Logger log = Logger('NotificationBloc');
  final _listNotifications = PublishSubject<List<NotificationElement>>();
  final _isEmptyList = PublishSubject<bool>();
  final _isLoggedIn = PublishSubject<bool>();

  Observable<List<NotificationElement>> get getNotificationList =>
      _listNotifications.stream;

  Observable<bool> get isLoggedIn => _isLoggedIn.stream;

  Observable<bool> get isEmptyList => _isEmptyList.stream;

  NotificationBloc({NotificationsRepository notificationsRepository})
      : _notificationsRepository =
            notificationsRepository ?? NotificationsRepository();

  void fetchNotifications() {
    _notificationsRepository
        .getNotificationList()
        .then((List<NotificationElement> list) {
          if (list.length > 0) {
            _listNotifications.add(list);
          } else {
            _isEmptyList.add(true);
          }
        })
        .catchError((e) => _handleErrorCase,
            test: (e) => e is AuthorizationException)
        .catchError((e) {
          log.shout("Error occurred while fetching notifications $e");
          _listNotifications.sink.addError("$e");
        });
  }
  void _handleErrorCase(e) {
     log.shout("Session invalid: $e");
     _isLoggedIn.sink.add(false);
     _listNotifications.sink.addError("Error");
 }
}

这是我的存储库代码的样子:

Notifications_repository.dart

import 'dart:async';

import 'package:logging/logging.dart';
import 'package:sunapsis/datasource/dataobjects/notification.dart';
import 'package:sunapsis/datasource/db/sunapsis_db_provider.dart';
import 'package:sunapsis/datasource/network/api_response.dart';
import 'package:sunapsis/datasource/network/sunapsis_api_provider.dart';
import 'package:sunapsis/utils/authorization_exception.dart';

/// Repository class which makes available all notifications related API functions
/// for server calls and database calls
class NotificationsRepository {
  final Logger log = Logger('NotificationsRepository');
  final SunapsisApiProvider apiProvider;
  final SunapsisDbProvider dbProvider;

  /// Optional [SunapsisApiProvider] and [SunapsisDbProvider] instances expected for unit testing
  /// If instances are not provided - default case - a new instance is created
  NotificationsRepository({SunapsisApiProvider api, SunapsisDbProvider db})
      : apiProvider = api ?? SunapsisApiProvider(),
        dbProvider = db ?? SunapsisDbProvider();

  /// Returns a [Future] of [List] of [NotificationElement]
  /// Tries to first look for notifications on the db
  /// if notifications are found that list is returned
  /// else a server call is made to fetch notifications
  Future<List<NotificationElement>> getNotificationList([int currentTime]) {
    return dbProvider.fetchNotifications().then(
        (List<NotificationElement> notifications) {
      if (notifications.length == 0) {
        return getNotificationsListFromServer(currentTime);
      }
      return notifications;
    }, onError: (_) {
      return getNotificationsListFromServer(currentTime);
    });
  }
}

该函数getNotificationsListFromServer应该抛出AuthorizationException应该通过getNotificationList

这是因前面提到的错误而失败的测试用例:

test('getNotification observable gets error on AuthorizationException',
    () async {
  when(mockNotificationsRepository.getNotificationList())
      .thenThrow(AuthorizationException("test error", "test description"));
  scheduleMicrotask(() => notificationBloc.fetchNotifications());
  await expectLater(
      notificationBloc.getNotificationList, emitsError("Error"));
});

这就是自定义异常的样子:

授权异常.dart

class AuthorizationException implements Exception {
  final String error;

  final String description;

  AuthorizationException(this.error, this.description);

  String toString() {
    var header = 'sunapsis Authorization error ($error)';
    if (description != null) {
      header = '$header: $description';
    }
    return '$header';
  }
}

PS:当我测试我的存储库类和抛出自定义异常的函数时,这些测试通过了。

test('throws AuthorizationException on invalidSession()', () async {
  when(mockSunapsisDbProvider.fetchNotifications())
      .thenAnswer((_) => Future.error("Error"));
  when(mockSunapsisDbProvider.getCachedLoginSession(1536333713))
      .thenAnswer((_) => Future.value(authorization));
  when(mockSunapsisApiProvider.getNotifications(authHeader))
      .thenAnswer((_) => Future.value(ApiResponse.invalidSession()));
  expect(notificationsRepository.getNotificationList(1536333713),
      throwsA(TypeMatcher<AuthorizationException>()));
});

上述测试通过并按预期工作。

我是一名刚毕业的大学毕业生,正在从事我的第一个全职工作,我可能做错了什么。我将非常感谢任何反馈或帮助,一切都有帮助。感谢您调查这个问题。

4

2 回答 2

1

我认为您使用了错误的TypeMatcher课程。您需要使用测试框架中的那个,而不是 Flutter 框架中的那个。

import 'package:flutter_test/flutter_test.dart';
import 'package:matcher/matcher.dart';

class AuthorizationException implements Exception {
  const AuthorizationException();
}

Future<List<String>> getNotificationList(int id) async {
  throw AuthorizationException();
}

void main() {
  test('getNotification observable gets error on AuthorizationException',
  () async {
    expect(getNotificationList(1536333713),
      throwsA(const TypeMatcher<AuthorizationException>()));
  });
}
于 2018-09-12T16:20:56.950 回答
0

thenThrow用于抛出异常,但由于模拟方法返回 Future 您应该使用thenAnswer.

测试将是这样的:

test('getNotification observable gets error on AuthorizationException', () async {

  // Using thenAnswer to throw an exception:
  when(mockNotificationsRepository.getNotificationList())
      .thenAnswer((_) async => throw AuthorizationException("test error", "test description"));

  scheduleMicrotask(() => notificationBloc.fetchNotifications());
  await expectLater(notificationBloc.getNotificationList, emitsError("Error"));
});
于 2021-10-19T21:11:53.347 回答