asyncredux-testing-basics
Write unit tests for AsyncRedux actions using the Store directly. Covers creating test stores with initial state, using `dispatchAndWait()`, checking state after actions, verifying action errors via ActionStatus, and testing async actions.
Best use case
asyncredux-testing-basics is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Write unit tests for AsyncRedux actions using the Store directly. Covers creating test stores with initial state, using `dispatchAndWait()`, checking state after actions, verifying action errors via ActionStatus, and testing async actions.
Teams using asyncredux-testing-basics 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-basics/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How asyncredux-testing-basics Compares
| Feature / Agent | asyncredux-testing-basics | 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?
Write unit tests for AsyncRedux actions using the Store directly. Covers creating test stores with initial state, using `dispatchAndWait()`, checking state after actions, verifying action errors via ActionStatus, and testing async actions.
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 AsyncRedux Actions
The recommended approach for testing AsyncRedux is to use the `Store` directly rather than the deprecated `StoreTester`. This provides a clean, straightforward testing pattern.
## Creating a Test Store
Create a store with test-specific initial state:
```dart
import 'package:flutter_test/flutter_test.dart';
import 'package:async_redux/async_redux.dart';
void main() {
test('should increment counter', () async {
// Create store with initial state
var store = Store<AppState>(
initialState: AppState(counter: 0, name: ''),
);
// Test your actions here
});
}
```
For test isolation, create a fresh store in each test:
```dart
void main() {
late Store<AppState> store;
setUp(() {
store = Store<AppState>(
initialState: AppState.initialState(),
);
});
tearDown(() {
store.shutdown();
});
// Tests go here
}
```
## Basic Test Pattern: Dispatch, Wait, Expect
Use `dispatchAndWait()` to dispatch an action and wait for it to complete:
```dart
test('SaveNameAction updates the name', () async {
var store = Store<AppState>(
initialState: AppState(name: ''),
);
await store.dispatchAndWait(SaveNameAction('John'));
expect(store.state.name, 'John');
});
```
## Testing Async Actions
Async actions work the same way - `dispatchAndWait()` returns only when the action fully completes:
```dart
class FetchUserAction extends ReduxAction<AppState> {
final String userId;
FetchUserAction(this.userId);
Future<AppState?> reduce() async {
var user = await api.fetchUser(userId);
return state.copy(user: user);
}
}
test('FetchUserAction loads user data', () async {
var store = Store<AppState>(
initialState: AppState(user: null),
);
await store.dispatchAndWait(FetchUserAction('123'));
expect(store.state.user, isNotNull);
expect(store.state.user!.id, '123');
});
```
## Testing Multiple Actions in Parallel
Use `dispatchAndWaitAll()` to dispatch multiple actions and wait for all to complete:
```dart
test('can buy and sell stocks in parallel', () async {
var store = Store<AppState>(
initialState: AppState(portfolio: Portfolio.empty()),
);
await store.dispatchAndWaitAll([
BuyAction('IBM', quantity: 10),
SellAction('TSLA', quantity: 5),
]);
expect(store.state.portfolio.holdings['IBM'], 10);
expect(store.state.portfolio.holdings['TSLA'], isNull);
});
```
## Verifying Action Errors with ActionStatus
`dispatchAndWait()` returns an `ActionStatus` object that lets you verify if an action succeeded or failed:
```dart
test('SaveAction fails with invalid data', () async {
var store = Store<AppState>(
initialState: AppState.initialState(),
);
var status = await store.dispatchAndWait(SaveAction(amount: -100));
expect(status.isCompletedFailed, isTrue);
expect(status.isCompletedOk, isFalse);
});
```
### ActionStatus Properties
- **`isCompleted`**: Whether the action finished executing
- **`isCompletedOk`**: True if action finished without errors (both `before()` and `reduce()` completed successfully)
- **`isCompletedFailed`**: True if action threw an error
- **`originalError`**: The error thrown by `before()` or `reduce()`
- **`wrappedError`**: The error after `wrapError()` processing
- **`hasFinishedMethodBefore`**: Whether `before()` completed
- **`hasFinishedMethodReduce`**: Whether `reduce()` completed
- **`hasFinishedMethodAfter`**: Whether `after()` completed
## Testing UserException Errors
Test that actions throw appropriate `UserException` errors:
```dart
class TransferMoney extends ReduxAction<AppState> {
final double amount;
TransferMoney(this.amount);
AppState? reduce() {
if (amount <= 0) {
throw UserException('Amount must be positive.');
}
return state.copy(balance: state.balance - amount);
}
}
test('TransferMoney throws UserException for invalid amount', () async {
var store = Store<AppState>(
initialState: AppState(balance: 1000),
);
var status = await store.dispatchAndWait(TransferMoney(0));
expect(status.isCompletedFailed, isTrue);
var error = status.wrappedError;
expect(error, isA<UserException>());
expect((error as UserException).msg, 'Amount must be positive.');
});
```
## Testing Multiple Errors with Error Queue
When multiple actions fail, check the store's error queue:
```dart
test('multiple actions can fail', () async {
var store = Store<AppState>(
initialState: AppState.initialState(),
);
await store.dispatchAndWaitAll([
InvalidAction1(),
InvalidAction2(),
]);
// Check errors in the store's error queue
expect(store.errors.length, 2);
});
```
## Conditional Navigation After Action Success
A common pattern is navigating only after an action succeeds:
```dart
test('navigate only on successful save', () async {
var store = Store<AppState>(
initialState: AppState.initialState(),
);
var status = await store.dispatchAndWait(SaveAction(data: validData));
expect(status.isCompletedOk, isTrue);
// In real code: if (status.isCompletedOk) Navigator.pop(context);
});
```
## Testing State Unchanged on Error
When an action throws, state should remain unchanged:
```dart
test('state unchanged when action fails', () async {
var store = Store<AppState>(
initialState: AppState(counter: 5),
);
var initialState = store.state;
await store.dispatchAndWait(FailingAction());
// State should not have changed
expect(store.state.counter, 5);
expect(store.state, initialState);
});
```
## Using MockStore for Dependency Isolation
Use `MockStore` to mock specific actions in tests:
```dart
test('with mocked dependency action', () async {
var store = MockStore<AppState>(
initialState: AppState.initialState(),
mocks: {
// Disable the action (don't run it)
FetchFromServerAction: null,
// Or replace with custom state modification
FetchFromServerAction: (action, state) =>
state.copy(data: 'mocked data'),
},
);
await store.dispatchAndWait(ActionThatDependsOnFetch());
expect(store.state.data, 'mocked data');
});
```
## Advanced Wait Methods for Complex Tests
For complex async scenarios, use these additional wait methods:
```dart
// Wait for a specific state condition
await store.waitCondition((state) => state.isLoaded);
// Wait for all given action types to complete
await store.waitAllActionTypes([LoadAction, ProcessAction]);
// Wait for any action of given types to finish
await store.waitAnyActionTypeFinishes([LoadAction]);
// Wait until no actions are in progress
await store.waitAllActions([]);
```
## Test File Organization
Recommended naming convention for test files:
- Widget: `my_feature.dart`
- State tests: `my_feature_STATE_test.dart`
- Connector tests: `my_feature_CONNECTOR_test.dart`
- Presentation tests: `my_feature_PRESENTATION_test.dart`
## Complete Test Example
```dart
import 'package:flutter_test/flutter_test.dart';
import 'package:async_redux/async_redux.dart';
void main() {
group('IncrementAction', () {
late Store<AppState> store;
setUp(() {
store = Store<AppState>(
initialState: AppState(counter: 0),
);
});
test('increments counter by 1', () async {
await store.dispatchAndWait(IncrementAction());
expect(store.state.counter, 1);
});
test('increments counter multiple times', () async {
await store.dispatchAndWait(IncrementAction());
await store.dispatchAndWait(IncrementAction());
await store.dispatchAndWait(IncrementAction());
expect(store.state.counter, 3);
});
test('handles concurrent increments', () async {
await store.dispatchAndWaitAll([
IncrementAction(),
IncrementAction(),
IncrementAction(),
]);
expect(store.state.counter, 3);
});
});
group('FetchDataAction', () {
test('succeeds with valid response', () async {
var store = Store<AppState>(
initialState: AppState(data: null),
);
var status = await store.dispatchAndWait(FetchDataAction());
expect(status.isCompletedOk, isTrue);
expect(store.state.data, isNotNull);
});
test('fails gracefully on error', () async {
var store = Store<AppState>(
initialState: AppState(data: null),
);
var status = await store.dispatchAndWait(
FetchDataAction(simulateError: true),
);
expect(status.isCompletedFailed, isTrue);
expect(status.wrappedError, isA<UserException>());
expect(store.state.data, isNull); // State unchanged
});
});
}
```
## References
URLs from the documentation:
- https://asyncredux.com/flutter/testing/store-tester
- 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/testing-user-exceptions
- https://asyncredux.com/flutter/advanced-actions/action-status
- https://asyncredux.com/flutter/basics/async-actions
- https://asyncredux.com/flutter/basics/dispatching-actions
- https://asyncredux.com/flutter/basics/failed-actions
- https://asyncredux.com/flutter/advanced-actions/errors-thrown-by-actions
- https://asyncredux.com/flutter/basics/store
- 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.
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 ...
backtesting-frameworks
Build robust backtesting systems for trading strategies with proper handling of look-ahead bias, survivorship bias, and transaction costs. Use when developing trading algorithms, validating strateg...
axiom-ios-testing
Use when writing ANY test, debugging flaky tests, making tests faster, or asking about Swift Testing vs XCTest. Covers unit tests, UI tests, fast tests without simulator, async testing, test architecture.
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.