$npx -y skills add flutter/agent-plugins --skill dart-add-unit-testWrite and organize unit tests for functions, methods, and classes using package:test. Use when creating new logic or fixing bugs to ensure code remains correct and regression-free.
| 1 | # Testing Dart and Flutter Applications |
| 2 | |
| 3 | ## Contents |
| 4 | - [Structuring Test Files](#structuring-test-files) |
| 5 | - [Writing Tests](#writing-tests) |
| 6 | - [Executing Tests](#executing-tests) |
| 7 | - [Test Implementation Workflow](#test-implementation-workflow) |
| 8 | - [Examples](#examples) |
| 9 | |
| 10 | ## Structuring Test Files |
| 11 | Organize test files to mirror the `lib` directory structure to maintain predictability. |
| 12 | |
| 13 | * Place all test code within the `test` directory at the root of the package. |
| 14 | * Append `_test.dart` to the end of all test file names (e.g., `lib/src/utils.dart` should be tested in `test/src/utils_test.dart`). |
| 15 | * If writing integration tests, place them in an `integration_test` directory at the root of the package. |
| 16 | |
| 17 | ## Writing Tests |
| 18 | Utilize `package:test` as the standard testing library for Dart applications. |
| 19 | |
| 20 | * Import `package:test/test.dart` (or `package:flutter_test/flutter_test.dart` for Flutter). |
| 21 | * Group related tests using the `group()` function to provide shared context. |
| 22 | * Define individual test cases using the `test()` function. |
| 23 | * Validate outcomes using the `expect()` function alongside matchers (e.g., `equals()`, `isTrue`, `throwsA()`). |
| 24 | * Write asynchronous tests using standard `async`/`await` syntax. The test runner automatically waits for the `Future` to complete. |
| 25 | * Manage test setup and teardown using `setUp()` and `tearDown()` callbacks. |
| 26 | * If testing code that relies on dependency injection, use `package:mockito` alongside `package:test` to generate mock objects, configure fixed scenarios, and verify interactions. |
| 27 | |
| 28 | ## Executing Tests |
| 29 | Select the appropriate test runner based on the project type and test location. |
| 30 | |
| 31 | * If working on a pure Dart project, execute tests using the `dart test` command. |
| 32 | * If working on a Flutter project, execute tests using the `flutter test` command. |
| 33 | * If running integration tests, explicitly specify the directory path, as the default runner ignores it: `dart test integration_test` or `flutter test integration_test`. |
| 34 | |
| 35 | ## Test Implementation Workflow |
| 36 | |
| 37 | Follow this sequential workflow when implementing new test suites. Copy the checklist to track your progress. |
| 38 | |
| 39 | ### Task Progress |
| 40 | - [ ] 1. Create the test file in the `test/` directory, ensuring the `_test.dart` suffix. |
| 41 | - [ ] 2. Import `package:test/test.dart` and the target library. |
| 42 | - [ ] 3. Define a `main()` function. |
| 43 | - [ ] 4. Initialize shared resources or mocks using `setUp()`. |
| 44 | - [ ] 5. Write `test()` cases grouped by functionality using `group()`. |
| 45 | - [ ] 6. Execute the test suite using the appropriate CLI command. |
| 46 | - [ ] 7. **Feedback Loop**: Run test -> Review stack trace for failures -> Fix implementation or assertions -> Re-run until passing. |
| 47 | |
| 48 | ## Examples |
| 49 | |
| 50 | ### Standard Unit Test Suite |
| 51 | Demonstrates grouping, setup, synchronous, and asynchronous testing. |
| 52 | |
| 53 | ```dart |
| 54 | import 'package:test/test.dart'; |
| 55 | import 'package:my_package/calculator.dart'; |
| 56 | |
| 57 | void main() { |
| 58 | group('Calculator', () { |
| 59 | late Calculator calc; |
| 60 | |
| 61 | setUp(() { |
| 62 | calc = Calculator(); |
| 63 | }); |
| 64 | |
| 65 | test('adds two numbers correctly', () { |
| 66 | expect(calc.add(2, 3), equals(5)); |
| 67 | }); |
| 68 | |
| 69 | test('handles asynchronous operations', () async { |
| 70 | final result = await calc.fetchRemoteValue(); |
| 71 | expect(result, isNotNull); |
| 72 | expect(result, greaterThan(0)); |
| 73 | }); |
| 74 | }); |
| 75 | } |
| 76 | ``` |
| 77 | |
| 78 | ### Mocking with Mockito |
| 79 | Demonstrates configuring a mock object for dependency injection testing. |
| 80 | |
| 81 | ```dart |
| 82 | import 'package:test/test.dart'; |
| 83 | import 'package:mockito/mockito.dart'; |
| 84 | import 'package:mockito/annotations.dart'; |
| 85 | import 'package:my_package/api_client.dart'; |
| 86 | import 'package:my_package/data_service.dart'; |
| 87 | |
| 88 | // Generate the mock using build_runner: dart run build_runner build |
| 89 | @GenerateNiceMocks([MockSpec<ApiClient>()]) |
| 90 | import 'data_service_test.mocks.dart'; |
| 91 | |
| 92 | void main() { |
| 93 | group('DataService', () { |
| 94 | late MockApiClient mockApiClient; |
| 95 | late DataService dataService; |
| 96 | |
| 97 | setUp(() { |
| 98 | mockApiClient = MockApiClient(); |
| 99 | dataService = DataService(apiClient: mockApiClient); |
| 100 | }); |
| 101 | |
| 102 | test('returns parsed data on successful API call', () async { |
| 103 | // Configure the mock |
| 104 | when(mockApiClient.get('/data')).thenAnswer((_) async => '{"id": 1}'); |
| 105 | |
| 106 | // Execute the system under test |
| 107 | final result = await dataService.fetchData(); |
| 108 | |
| 109 | // Verify outcomes and interactions |
| 110 | expect(result.id, equals(1)); |
| 111 | verify(mockApiClient.get('/data')).called(1); |
| 112 | }); |
| 113 | }); |
| 114 | } |
| 115 | ``` |