flutter_stasis_test 1.0.0
flutter_stasis_test: ^1.0.0 copied to clipboard
Test helpers for flutter_stasis. captureStates, captureEvents, assertStateSequence and assertEventSequence.
flutter_stasis_test #
Test helpers for flutter_stasis.
Installation #
dev_dependencies:
flutter_stasis_test: ^1.0.0
flutter_test:
sdk: flutter
Why #
Testing a StasisViewModel is straightforward — call a method, check the state. But writing the capture/assertion loop manually gets repetitive:
// Without helpers — repeated in every test file
final states = <MyState>[];
vm.stateListenable.addListener(() => states.add(vm.state));
await vm.load();
expect(states[0].isLoading, isTrue);
expect(states[1].projects, hasLength(3));
With flutter_stasis_test:
await assertStateSequence(
listenable: vm.stateListenable,
act: vm.load,
expected: [
(s) => s.isLoading,
(s) => s.projects?.length == 3,
],
includeInitial: false,
);
captureStates #
Collects all state values emitted while act runs:
final states = await captureStates(
listenable: vm.stateListenable,
act: vm.load,
includeInitial: true, // include the state before act runs (default: true)
);
expect(states, hasLength(3)); // initial + loading + success
expect(states[1].isLoading, isTrue);
expect(states[2].projects, isNotEmpty);
captureEvents #
Collects all UiEvents emitted while act runs:
final events = await captureEvents(
stream: vm.events,
act: vm.save,
);
expect(events, [isA<ShowSnackBarEvent>(), isA<PopEvent>()]);
assertStateSequence #
Captures states and asserts each one with a predicate. Fails with a descriptive message if the length or any predicate doesn't match:
await assertStateSequence(
listenable: vm.stateListenable,
act: vm.load,
expected: [
(s) => s.isLoading,
(s) => s.projects?.length == 3,
],
includeInitial: false,
);
Using equalsValue for simple equality:
await assertStateSequence(
listenable: vm.stateListenable,
act: vm.setFilter,
expected: [
equalsValue(ProjectsState(filter: ProjectFilter.active, ...)),
],
includeInitial: false,
);
assertEventSequence #
Same as assertStateSequence but for events:
await assertEventSequence(
stream: vm.events,
act: vm.onSavePressed,
expected: [
(e) => e is ShowSnackBarEvent && e.message == 'Saved',
(e) => e is NavigateToEvent,
],
);
Full test example #
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_stasis_test/flutter_stasis_test.dart';
void main() {
late ProjectsViewModel vm;
late MockGetProjectsUseCase mockUseCase;
setUp(() {
mockUseCase = MockGetProjectsUseCase();
vm = ProjectsViewModel(mockUseCase);
});
tearDown(() => vm.dispose());
test('load emits loading then success', () async {
final fakeProjects = [Project(id: '1', name: 'Test')];
when(() => mockUseCase.call()).thenAnswer(
(_) async => Right(fakeProjects),
);
await assertStateSequence(
listenable: vm.stateListenable,
act: vm.load,
expected: [
(s) => s.isLoading,
(s) => s.projects == fakeProjects,
],
includeInitial: false,
);
});
test('load emits error on failure', () async {
when(() => mockUseCase.call()).thenAnswer(
(_) async => Left(AppFailure('Network error')),
);
await assertStateSequence(
listenable: vm.stateListenable,
act: vm.load,
expected: [
(s) => s.isLoading,
(s) => s.errorMessage == 'Network error',
],
includeInitial: false,
);
});
test('save emits snackbar event then navigates', () async {
await assertEventSequence(
stream: vm.events,
act: vm.save,
expected: [
(e) => e is ShowSnackBarEvent,
(e) => e is NavigateToEvent,
],
);
});
}
settle parameter #
If your command involves microtasks or delayed work, use settle to wait before collecting:
await assertStateSequence(
listenable: vm.stateListenable,
act: vm.loadWithDebounce,
expected: [...],
settle: const Duration(milliseconds: 500),
);
License #
MIT