asyncredux-testing-view-models
Test StoreConnector view-models in isolation. Covers creating view-models with `Vm.createFrom()`, testing view-model properties, testing callbacks that dispatch actions, and verifying state changes from callbacks.
Best use case
asyncredux-testing-view-models is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Test StoreConnector view-models in isolation. Covers creating view-models with `Vm.createFrom()`, testing view-model properties, testing callbacks that dispatch actions, and verifying state changes from callbacks.
Teams using asyncredux-testing-view-models should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/asyncredux-testing-view-models/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How asyncredux-testing-view-models Compares
| Feature / Agent | asyncredux-testing-view-models | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Test StoreConnector view-models in isolation. Covers creating view-models with `Vm.createFrom()`, testing view-model properties, testing callbacks that dispatch actions, and verifying state changes from callbacks.
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# Testing View-Models in AsyncRedux
View-models created by `VmFactory` can be tested in isolation without building widgets. Use `Vm.createFrom()` to instantiate the view-model directly, then verify properties and execute callbacks.
## Creating a View-Model for Testing
Use `Vm.createFrom()` with a store and factory instance:
```dart
import 'package:flutter_test/flutter_test.dart';
import 'package:async_redux/async_redux.dart';
test('view-model has correct properties', () {
var store = Store<AppState>(
initialState: AppState(name: 'Mary', counter: 5),
);
var vm = Vm.createFrom(store, CounterFactory());
expect(vm.counter, 5);
expect(vm.name, 'Mary');
});
```
**Important:** `Vm.createFrom()` can only be called once per factory instance. Create a new factory for each test.
## Testing View-Model Properties
Verify that the factory correctly transforms state into view-model properties:
```dart
class CounterViewModel extends Vm {
final int counter;
final String description;
final VoidCallback onIncrement;
CounterViewModel({
required this.counter,
required this.description,
required this.onIncrement,
}) : super(equals: [counter, description]);
}
class CounterFactory extends VmFactory<AppState, CounterConnector, CounterViewModel> {
@override
CounterViewModel fromStore() => CounterViewModel(
counter: state.counter,
description: 'Count is ${state.counter}',
onIncrement: () => dispatch(IncrementAction()),
);
}
test('factory transforms state correctly', () {
var store = Store<AppState>(
initialState: AppState(counter: 10),
);
var vm = Vm.createFrom(store, CounterFactory());
expect(vm.counter, 10);
expect(vm.description, 'Count is 10');
});
```
## Testing Callbacks That Dispatch Actions
When testing callbacks, invoke them and then use wait methods to verify actions were dispatched and state changed:
```dart
test('onIncrement dispatches IncrementAction', () async {
var store = Store<AppState>(
initialState: AppState(counter: 0),
);
var vm = Vm.createFrom(store, CounterFactory());
// Invoke the callback
vm.onIncrement();
// Wait for the action to complete
await store.waitActionType(IncrementAction);
// Verify state changed
expect(store.state.counter, 1);
});
```
## Wait Methods for Callback Testing
Several wait methods help verify callback behavior:
### waitActionType
Wait for a specific action type to finish:
```dart
test('callback dispatches expected action', () async {
var store = Store<AppState>(initialState: AppState(name: ''));
var vm = Vm.createFrom(store, UserFactory());
vm.onSave('John');
await store.waitActionType(SaveNameAction);
expect(store.state.name, 'John');
});
```
### waitAllActionTypes
Wait for multiple action types to complete:
```dart
test('callback triggers multiple actions', () async {
var store = Store<AppState>(initialState: AppState.initialState());
var vm = Vm.createFrom(store, CheckoutFactory());
vm.onCheckout();
await store.waitAllActionTypes([ValidateCartAction, ProcessPaymentAction]);
expect(store.state.orderCompleted, isTrue);
});
```
### waitAnyActionTypeFinishes
Wait for any matching action to finish, useful when testing actions that may or may not be dispatched:
```dart
test('refresh triggers data fetch', () async {
var store = Store<AppState>(initialState: AppState.initialState());
var vm = Vm.createFrom(store, DataFactory());
vm.onRefresh();
var action = await store.waitAnyActionTypeFinishes([FetchDataAction]);
expect(action, isA<FetchDataAction>());
expect(store.state.data, isNotEmpty);
});
```
### waitCondition
Wait for state to meet a specific condition:
```dart
test('loading completes when data is fetched', () async {
var store = Store<AppState>(initialState: AppState(isLoading: false, data: null));
var vm = Vm.createFrom(store, DataFactory());
vm.onLoad();
await store.waitCondition((state) => state.data != null);
expect(store.state.isLoading, isFalse);
expect(store.state.data, isNotNull);
});
```
### waitAllActions
Wait until no actions are in progress:
```dart
test('all actions complete', () async {
var store = Store<AppState>(initialState: AppState.initialState());
var vm = Vm.createFrom(store, BatchFactory());
vm.onProcessBatch();
await store.waitAllActions([]);
expect(store.state.batchProcessed, isTrue);
});
```
## Testing Callbacks with Action Status
Verify that callbacks dispatch actions that succeed or fail appropriately:
```dart
test('save callback handles errors', () async {
var store = Store<AppState>(
initialState: AppState(data: ''),
);
var vm = Vm.createFrom(store, FormFactory());
// Trigger save with invalid data
vm.onSave('');
// dispatchAndWait returns ActionStatus, but when testing callbacks,
// use waitActionType and check store.errors
await store.waitActionType(SaveAction);
// Check if action failed
expect(store.errors, isNotEmpty);
});
```
## Testing Async Callbacks
Async callbacks work the same way - wait for the dispatched actions:
```dart
class UserFactory extends VmFactory<AppState, UserConnector, UserViewModel> {
@override
UserViewModel fromStore() => UserViewModel(
user: state.user,
onRefresh: () => dispatch(FetchUserAction()),
);
}
test('onRefresh loads user data', () async {
var store = Store<AppState>(
initialState: AppState(user: null),
);
var vm = Vm.createFrom(store, UserFactory());
vm.onRefresh();
await store.waitActionType(FetchUserAction);
expect(store.state.user, isNotNull);
});
```
## Testing with Mocked Actions
Use `MockStore` to mock actions triggered by callbacks:
```dart
test('callback with mocked dependency', () async {
var store = MockStore<AppState>(
initialState: AppState(data: null),
mocks: {
// Mock the API call to return test data
FetchDataAction: (action, state) => state.copy(data: 'mocked data'),
},
);
var vm = Vm.createFrom(store, DataFactory());
vm.onFetch();
await store.waitActionType(FetchDataAction);
expect(store.state.data, 'mocked data');
});
```
## Testing onInit and onDispose Lifecycle
Use `ConnectorTester` to test lifecycle callbacks without building widgets:
```dart
class MyScreen extends StatelessWidget {
@override
Widget build(BuildContext context) => StoreConnector<AppState, MyViewModel>(
vm: () => MyFactory(),
onInit: (store) => store.dispatch(StartPollingAction()),
onDispose: (store) => store.dispatch(StopPollingAction()),
builder: (context, vm) => MyWidget(vm: vm),
);
}
test('onInit dispatches StartPollingAction', () async {
var store = Store<AppState>(initialState: AppState.initialState());
var connectorTester = store.getConnectorTester(MyScreen());
connectorTester.runOnInit();
var action = await store.waitAnyActionTypeFinishes([StartPollingAction]);
expect(action, isA<StartPollingAction>());
});
test('onDispose dispatches StopPollingAction', () async {
var store = Store<AppState>(initialState: AppState.initialState());
var connectorTester = store.getConnectorTester(MyScreen());
connectorTester.runOnDispose();
var action = await store.waitAnyActionTypeFinishes([StopPollingAction]);
expect(action, isA<StopPollingAction>());
});
```
## Complete Test Example
```dart
import 'package:flutter_test/flutter_test.dart';
import 'package:async_redux/async_redux.dart';
// View-Model
class TodoViewModel extends Vm {
final List<String> todos;
final bool isLoading;
final void Function(String) onAddTodo;
final void Function(int) onRemoveTodo;
final VoidCallback onRefresh;
TodoViewModel({
required this.todos,
required this.isLoading,
required this.onAddTodo,
required this.onRemoveTodo,
required this.onRefresh,
}) : super(equals: [todos, isLoading]);
}
// Factory
class TodoFactory extends VmFactory<AppState, TodoConnector, TodoViewModel> {
@override
TodoViewModel fromStore() => TodoViewModel(
todos: state.todos,
isLoading: state.isLoading,
onAddTodo: (text) => dispatch(AddTodoAction(text)),
onRemoveTodo: (index) => dispatch(RemoveTodoAction(index)),
onRefresh: () => dispatch(FetchTodosAction()),
);
}
void main() {
group('TodoFactory', () {
late Store<AppState> store;
setUp(() {
store = Store<AppState>(
initialState: AppState(todos: [], isLoading: false),
);
});
test('creates view-model with correct initial properties', () {
var vm = Vm.createFrom(store, TodoFactory());
expect(vm.todos, isEmpty);
expect(vm.isLoading, isFalse);
});
test('onAddTodo dispatches AddTodoAction', () async {
var vm = Vm.createFrom(store, TodoFactory());
vm.onAddTodo('Buy milk');
await store.waitActionType(AddTodoAction);
expect(store.state.todos, contains('Buy milk'));
});
test('onRemoveTodo dispatches RemoveTodoAction', () async {
store = Store<AppState>(
initialState: AppState(todos: ['Task 1', 'Task 2'], isLoading: false),
);
var vm = Vm.createFrom(store, TodoFactory());
vm.onRemoveTodo(0);
await store.waitActionType(RemoveTodoAction);
expect(store.state.todos, ['Task 2']);
});
test('onRefresh fetches todos', () async {
var vm = Vm.createFrom(store, TodoFactory());
vm.onRefresh();
await store.waitCondition((state) => !state.isLoading);
expect(store.state.todos, isNotEmpty);
});
});
}
```
## Test Organization
Follow the recommended naming convention for test files:
- Widget: `todo_screen.dart`
- Connector: `todo_screen_connector.dart`
- State tests: `todo_screen_STATE_test.dart`
- Connector tests: `todo_screen_CONNECTOR_test.dart`
- Presentation tests: `todo_screen_PRESENTATION_test.dart`
Connector tests focus on view-model logic - verifying properties are correctly derived from state and callbacks dispatch appropriate actions.
## References
URLs from the documentation:
- https://asyncredux.com/flutter/testing/testing-the-view-model
- https://asyncredux.com/flutter/testing/testing-oninit-ondispose
- https://asyncredux.com/flutter/testing/dispatch-wait-and-expect
- https://asyncredux.com/flutter/testing/test-files
- https://asyncredux.com/flutter/testing/mocking
- https://asyncredux.com/flutter/testing/store-tester
- https://asyncredux.com/flutter/connector/store-connector
- https://asyncredux.com/flutter/connector/advanced-view-model
- https://asyncredux.com/flutter/connector/connector-pattern
- https://asyncredux.com/flutter/miscellaneous/advanced-waitingRelated Skills
e2e-testing
End-to-end testing workflow with Playwright for browser automation, visual regression, cross-browser testing, and CI/CD integration.
e2e-testing-patterns
Master end-to-end testing with Playwright and Cypress to build reliable test suites that catch bugs, improve confidence, and enable fast deployment. Use when implementing E2E tests, debugging flaky tests, or establishing testing standards.
dotnet-uno-testing
Tests Uno Platform apps. Playwright for WASM, platform-specific patterns, runtime heads.
cve-testing
CVE vulnerability testing coordinator that identifies technology stacks, researches known vulnerabilities, and tests applications for exploitable CVEs using public exploits and proof-of-concept code.
cui-javascript-unit-testing
Jest unit testing standards covering configuration, test structure, testing patterns, and coverage requirements
configure-ux-testing
Check and configure UX testing infrastructure (Playwright, accessibility, visual regression)
comprehensive-unit-testing-with-pytest
Aims for high test coverage using pytest, testing both common and edge cases.
code-reviewer
Elite code review expert specializing in modern AI-powered code analysis, security vulnerabilities, performance optimization, and production reliability. Masters static analysis tools, security scanning, and configuration review with 2024/2025 best practices. Use PROACTIVELY for code quality assurance.
code-review-patterns
Internal skill. Use cc10x-router for all development tasks.
code-review
Reviews code changes for quality, security, and best practices. Auto-invoke when implementation is complete and the workflow reaches the review step (step 9), or when changes are ready for pre-PR review.
Burp Suite Web Application Testing
This skill should be used when the user asks to "intercept HTTP traffic", "modify web requests", "use Burp Suite for testing", "perform web vulnerability scanning", "test with Burp Repeater", "analyze HTTP history", or "configure proxy for web testing". It provides comprehensive guidance for using Burp Suite's core features for web application security testing.
burp-suite-testing
This skill should be used when the user asks to "intercept HTTP traffic", "modify web requests", "use Burp Suite for testing", "perform web vulnerability scanning", "test with Burp ...