颤振块测试
Flutter Bloc Test
我在一个团队中工作,我们正在使用 flutter bloc 进行状态管理,但是我们的一个测试用例根本没有任何意义。集团本身运作良好,但测试没有任何意义。
下面是集团本身。
import 'package:vaccify/features/logout/logout.dart';
import 'package:vaccify/features/side_navigation_bar/domain/get_user_name.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:vaccify/features/side_navigation_bar/domain/get_user_profile_pic_url.dart';
part 'auth_event.dart';
part 'auth_state.dart';
class AuthBloc extends Bloc<AuthEvent, AuthState> {
GetUserName getUserName;
GetUserProfilePic getUserProfilePic;
Logout logout;
AuthBloc(
{required this.getUserName,
required this.logout,
required this.getUserProfilePic})
: super(NotLoggedIn()) {
on<AuthLogIn>(_onLogIn);
on<AuthLogOut>(_onLogOut);
}
void _onLogIn(
AuthLogIn event,
Emitter<AuthState> emit,
) async {
final name = await getUserName();
final profilePic = await getUserProfilePic();
emit(LoggedIn(name, profilePic));
}
void _onLogOut(
AuthLogOut event,
Emitter<AuthState> emit,
) {
logout();
emit(NotLoggedIn());
}
}
现在进行测试
import 'package:bloc_test/bloc_test.dart';
import 'package:vaccify/core/bloc/authentication/auth_bloc.dart';
import 'package:vaccify/features/logout/logout.dart';
import 'package:vaccify/features/side_navigation_bar/domain/get_user_name.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:vaccify/features/side_navigation_bar/domain/get_user_profile_pic_url.dart';
class MockGetUserName extends Mock implements GetUserName {}
class MockGetProfilePicUrl extends Mock implements GetUserProfilePic {}
class MockLogout extends Mock implements Logout {}
void main() {
late AuthBloc authBloc;
late MockGetUserName mockGetUserName;
late MockLogout mockLogout;
late MockGetProfilePicUrl mockGetProfilePicUrl;
setUp(() {
mockGetUserName = MockGetUserName();
mockLogout = MockLogout();
mockGetProfilePicUrl = MockGetProfilePicUrl();
authBloc = AuthBloc(
getUserName: mockGetUserName,
logout: mockLogout,
getUserProfilePic: mockGetProfilePicUrl);
});
/// Contains bloc tests for the [AuthBloc] class
group(
'AuthBloc',
() {
/// Tests that a [LoggedIn] state occurs when the [AuthLogIn] is called.
blocTest(
'login',
build: () {
when(() => mockGetUserName()).thenAnswer((_) async => "Hello");
when(() => mockGetProfilePicUrl()).thenAnswer((_) async => "");
return authBloc;
},
act: (AuthBloc bloc) {
bloc.add(AuthLogIn());
},
expect: () => [isA<LoggedIn>()],
);
/// Tests that a [NotLoggedIn] state occurs when the [AuthLogOut] is called.
blocTest(
'logout',
build: () {
when(() => mockGetUserName()).thenAnswer((_) async => "John Smith");
when(() => mockGetProfilePicUrl()).thenAnswer((_) async => "");
when(() => mockLogout()).thenAnswer((_) async {});
return authBloc;
},
act: (AuthBloc bloc) {
bloc.add(AuthLogIn());
bloc.add(AuthLogOut());
},
expect: () => [
isA<LoggedIn>(),
isA<NotLoggedIn>(),
],
);
},
);
}
第一个测试 'login' 工作正常并通过,因为我们添加了一个事件 "AuthLogin()" 然后我们期望 LoggedIn州
第二个测试 'logout' 是问题,因为我们要添加两个事件 AuthLogin() & AuthLogOut() 因为您必须登录才能登出。
然后我们分别期望 LoggedIn 和 NotLoggedIn 状态。
测试失败并显示以下消息
预期:[<<'LoggedIn'>> 的实例,<<'NotLoggedIn'>> 的实例>>]
实际:['NotLoggedIn' 的实例,'LoggedIn' 的实例]
有趣的是,当我们交换两者时期望测试通过...如下所示
expect: () => [
isA<NotLoggedIn>(),
isA<LoggedIn>(),
],
任何建议或指导将不胜感激。
谢谢大家
如果您添加 AuthLogIn
事件只是为了让您的集团为第二个事件做准备,您可以使用 blocTest
中的 seed
。像这样:
blocTest(
'logout',
build: () {
when(() => mockLogout()).thenAnswer((_) async {});
return authBloc;
},
seed: () => LoggedIn(),
act: (AuthBloc bloc) {
bloc.add(AuthLogOut());
},
expect: () => [
isA<NotLoggedIn>(),
],
);
关于您的问题,我认为在添加两个事件 (await Future.delayed(Duration(seconds: 1))
) 之间使用延迟可以解决问题。请注意,Bloc
默认情况下同时转换事件,在您的情况下,首先处理注销(它比登录功能完成得更快),然后处理您的登录事件,这导致了问题。
我在一个团队中工作,我们正在使用 flutter bloc 进行状态管理,但是我们的一个测试用例根本没有任何意义。集团本身运作良好,但测试没有任何意义。
下面是集团本身。
import 'package:vaccify/features/logout/logout.dart';
import 'package:vaccify/features/side_navigation_bar/domain/get_user_name.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:vaccify/features/side_navigation_bar/domain/get_user_profile_pic_url.dart';
part 'auth_event.dart';
part 'auth_state.dart';
class AuthBloc extends Bloc<AuthEvent, AuthState> {
GetUserName getUserName;
GetUserProfilePic getUserProfilePic;
Logout logout;
AuthBloc(
{required this.getUserName,
required this.logout,
required this.getUserProfilePic})
: super(NotLoggedIn()) {
on<AuthLogIn>(_onLogIn);
on<AuthLogOut>(_onLogOut);
}
void _onLogIn(
AuthLogIn event,
Emitter<AuthState> emit,
) async {
final name = await getUserName();
final profilePic = await getUserProfilePic();
emit(LoggedIn(name, profilePic));
}
void _onLogOut(
AuthLogOut event,
Emitter<AuthState> emit,
) {
logout();
emit(NotLoggedIn());
}
}
现在进行测试
import 'package:bloc_test/bloc_test.dart';
import 'package:vaccify/core/bloc/authentication/auth_bloc.dart';
import 'package:vaccify/features/logout/logout.dart';
import 'package:vaccify/features/side_navigation_bar/domain/get_user_name.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:vaccify/features/side_navigation_bar/domain/get_user_profile_pic_url.dart';
class MockGetUserName extends Mock implements GetUserName {}
class MockGetProfilePicUrl extends Mock implements GetUserProfilePic {}
class MockLogout extends Mock implements Logout {}
void main() {
late AuthBloc authBloc;
late MockGetUserName mockGetUserName;
late MockLogout mockLogout;
late MockGetProfilePicUrl mockGetProfilePicUrl;
setUp(() {
mockGetUserName = MockGetUserName();
mockLogout = MockLogout();
mockGetProfilePicUrl = MockGetProfilePicUrl();
authBloc = AuthBloc(
getUserName: mockGetUserName,
logout: mockLogout,
getUserProfilePic: mockGetProfilePicUrl);
});
/// Contains bloc tests for the [AuthBloc] class
group(
'AuthBloc',
() {
/// Tests that a [LoggedIn] state occurs when the [AuthLogIn] is called.
blocTest(
'login',
build: () {
when(() => mockGetUserName()).thenAnswer((_) async => "Hello");
when(() => mockGetProfilePicUrl()).thenAnswer((_) async => "");
return authBloc;
},
act: (AuthBloc bloc) {
bloc.add(AuthLogIn());
},
expect: () => [isA<LoggedIn>()],
);
/// Tests that a [NotLoggedIn] state occurs when the [AuthLogOut] is called.
blocTest(
'logout',
build: () {
when(() => mockGetUserName()).thenAnswer((_) async => "John Smith");
when(() => mockGetProfilePicUrl()).thenAnswer((_) async => "");
when(() => mockLogout()).thenAnswer((_) async {});
return authBloc;
},
act: (AuthBloc bloc) {
bloc.add(AuthLogIn());
bloc.add(AuthLogOut());
},
expect: () => [
isA<LoggedIn>(),
isA<NotLoggedIn>(),
],
);
},
);
}
第一个测试 'login' 工作正常并通过,因为我们添加了一个事件 "AuthLogin()" 然后我们期望 LoggedIn州
第二个测试 'logout' 是问题,因为我们要添加两个事件 AuthLogin() & AuthLogOut() 因为您必须登录才能登出。 然后我们分别期望 LoggedIn 和 NotLoggedIn 状态。
测试失败并显示以下消息
预期:[<<'LoggedIn'>> 的实例,<<'NotLoggedIn'>> 的实例>>]
实际:['NotLoggedIn' 的实例,'LoggedIn' 的实例]
有趣的是,当我们交换两者时期望测试通过...如下所示
expect: () => [
isA<NotLoggedIn>(),
isA<LoggedIn>(),
],
任何建议或指导将不胜感激。
谢谢大家
如果您添加 AuthLogIn
事件只是为了让您的集团为第二个事件做准备,您可以使用 blocTest
中的 seed
。像这样:
blocTest(
'logout',
build: () {
when(() => mockLogout()).thenAnswer((_) async {});
return authBloc;
},
seed: () => LoggedIn(),
act: (AuthBloc bloc) {
bloc.add(AuthLogOut());
},
expect: () => [
isA<NotLoggedIn>(),
],
);
关于您的问题,我认为在添加两个事件 (await Future.delayed(Duration(seconds: 1))
) 之间使用延迟可以解决问题。请注意,Bloc
默认情况下同时转换事件,在您的情况下,首先处理注销(它比登录功能完成得更快),然后处理您的登录事件,这导致了问题。