원클릭으로
setup-test
Set up proper test configuration for Cubits using bloc_superpowers with Superpowers.clear() and Completers
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Set up proper test configuration for Cubits using bloc_superpowers with Superpowers.clear() and Completers
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
Add custom error handling with catchError to a mix() call for logging, error conversion, or suppression
Add internet connectivity checking before executing a Cubit method with optional retry
Add debounce to delay method execution until after a period of inactivity for search or validation
Add EffectQueue to state for triggering multiple sequential one-time UI effects
Add Effect fields to state class for one-time UI notifications like snackbars, navigation, or form clearing
Add error state handling to widgets using context.isFailed() and context.getException()
| name | setup-test |
| description | Set up proper test configuration for Cubits using bloc_superpowers with Superpowers.clear() and Completers |
This skill sets up proper test configuration for Cubits using bloc_superpowers.
Configures tests to:
Always call Superpowers.clear() in setUp to reset state between tests:
import 'package:bloc_superpowers/bloc_superpowers.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
setUp(() {
Superpowers.clear(); // IMPORTANT: Reset state between tests
});
// ... tests
}
import 'package:bloc_superpowers/bloc_superpowers.dart';
import 'package:bloc_test/bloc_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
class MockApi extends Mock implements Api {}
void main() {
late UserCubit cubit;
late MockApi mockApi;
setUp(() {
Superpowers.clear(); // Reset Superpowers state
mockApi = MockApi();
cubit = UserCubit(api: mockApi);
});
tearDown(() {
cubit.close();
});
group('UserCubit', () {
test('initial state is correct', () {
expect(cubit.state, const UserState());
});
// ... more tests
});
}
Use Completer to control when async operations complete:
test('loadUser sets loading state while fetching', () async {
final completer = Completer<User>();
// Mock returns a future that we control
when(() => mockApi.getUser()).thenAnswer((_) => completer.future);
// Start the operation
cubit.loadUser();
// Wait for next event loop
await Future.delayed(Duration.zero);
// Check loading state
expect(Superpowers.isWaiting(UserCubit), isTrue);
// Complete the operation
completer.complete(User(name: 'John'));
// Wait for completion
await Future.delayed(Duration.zero);
// Check final state
expect(Superpowers.isWaiting(UserCubit), isFalse);
expect(cubit.state.user?.name, 'John');
});
test('isWaiting is true during load', () async {
final completer = Completer<User>();
when(() => mockApi.getUser()).thenAnswer((_) => completer.future);
cubit.loadUser();
await Future.delayed(Duration.zero);
// Use static method
expect(Superpowers.isWaiting(UserCubit), isTrue);
completer.complete(User(name: 'John'));
await Future.delayed(Duration.zero);
expect(Superpowers.isWaiting(UserCubit), isFalse);
});
test('per-item loading state', () async {
final completer = Completer<void>();
when(() => mockApi.deleteItem('123')).thenAnswer((_) => completer.future);
cubit.deleteItem('123');
await Future.delayed(Duration.zero);
// Check specific item key
expect(Superpowers.isWaiting((DeleteItem, '123')), isTrue);
expect(Superpowers.isWaiting((DeleteItem, '456')), isFalse);
completer.complete();
await Future.delayed(Duration.zero);
expect(Superpowers.isWaiting((DeleteItem, '123')), isFalse);
});
test('isFailed is true after error', () async {
when(() => mockApi.getUser()).thenThrow(Exception('Network error'));
cubit.loadUser();
await Future.delayed(Duration.zero);
expect(Superpowers.isFailed(UserCubit), isTrue);
expect(Superpowers.getException(UserCubit), isA<UserException>());
});
test('error clears on retry', () async {
// First call fails
when(() => mockApi.getUser()).thenThrow(Exception('Network error'));
cubit.loadUser();
await Future.delayed(Duration.zero);
expect(Superpowers.isFailed(UserCubit), isTrue);
// Second call succeeds
when(() => mockApi.getUser()).thenAnswer((_) async => User(name: 'John'));
cubit.loadUser();
await Future.delayed(Duration.zero);
expect(Superpowers.isFailed(UserCubit), isFalse);
expect(cubit.state.user?.name, 'John');
});
blocTest<UserCubit, UserState>(
'emits user when loadUser succeeds',
setUp: () {
Superpowers.clear(); // Reset in setUp
when(() => mockApi.getUser()).thenAnswer(
(_) async => User(name: 'John'),
);
},
build: () => UserCubit(api: mockApi),
act: (cubit) => cubit.loadUser(),
expect: () => [
UserState(user: User(name: 'John')),
],
);
test('effect is emitted on success', () async {
when(() => mockApi.saveUser(any())).thenAnswer((_) async {});
cubit.saveUser(User(name: 'John'));
await Future.delayed(Duration.zero);
// Check effect was emitted
expect(cubit.state.successMessageEffect.isNotSpent, isTrue);
// Consume the effect
final message = cubit.state.successMessageEffect.consume();
expect(message, 'User saved!');
// Effect is now spent
expect(cubit.state.successMessageEffect.isSpent, isTrue);
});
import 'package:bloc_superpowers/bloc_superpowers.dart';
import 'package:bloc_test/bloc_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
class MockApi extends Mock implements Api {}
void main() {
late ProductCubit cubit;
late MockApi mockApi;
setUp(() {
Superpowers.clear();
mockApi = MockApi();
cubit = ProductCubit(api: mockApi);
});
tearDown(() {
cubit.close();
});
group('loadProducts', () {
test('sets loading state while fetching', () async {
final completer = Completer<List<Product>>();
when(() => mockApi.getProducts()).thenAnswer((_) => completer.future);
cubit.loadProducts();
await Future.delayed(Duration.zero);
expect(Superpowers.isWaiting(ProductCubit), isTrue);
completer.complete([Product(id: '1', name: 'Widget')]);
await Future.delayed(Duration.zero);
expect(Superpowers.isWaiting(ProductCubit), isFalse);
expect(cubit.state.products.length, 1);
});
test('sets error state on failure', () async {
when(() => mockApi.getProducts()).thenThrow(Exception('Error'));
cubit.loadProducts();
await Future.delayed(Duration.zero);
expect(Superpowers.isFailed(ProductCubit), isTrue);
});
});
group('deleteProduct', () {
test('tracks loading per product', () async {
final completer = Completer<void>();
when(() => mockApi.deleteProduct('1')).thenAnswer((_) => completer.future);
cubit.deleteProduct('1');
await Future.delayed(Duration.zero);
expect(Superpowers.isWaiting((DeleteProduct, '1')), isTrue);
expect(Superpowers.isWaiting((DeleteProduct, '2')), isFalse);
completer.complete();
await Future.delayed(Duration.zero);
expect(Superpowers.isWaiting((DeleteProduct, '1')), isFalse);
});
});
}
Superpowers.clear() in setUp - Prevents state leaking between testsCompleter for async control - Allows testing intermediate statesFuture.delayed(Duration.zero) - Allows async operations to processisWaiting and isFailed - Cover loading and error statesAsk the user: