Dart Testing Suite
Contents
Unit Testing
Utilize package:test as the standard testing library for Dart CLI and backend applications.
- Organize test files to mirror the
libdirectory structure. Append_test.dartto test files. - Group related tests using the
group()function. - Use
setUp()andtearDown()for shared test state. - Write pure logic inside Domain/BLoC and extract to static pure functions for 100% unit test coverage.
- Use string interpolation for test group names:
group('$ClassName', ...) - Structure each test using the Arrange-Act-Assert (Given-When-Then) convention:
- Arrange: Set up mocks, create instances, prepare input data.
- Act: Call the method under test.
- Assert: Verify the output or side effects.
- Separate setup from verification to keep tests readable and maintainable.
Mocking Dependencies
mocktail vs mockito Decision
| Criteria | mocktail (Recommended) |
mockito |
|---|---|---|
| Code generation | Not needed | Requires build_runner |
| Setup speed | Fast (extend Mock) |
Slower (annotation + codegen) |
| Null safety | Full support | Full support |
| API style | when(() => mock.method()) |
when(mock.method()) |
| Best for | Most projects, fast iteration | Large projects with complex APIs |
Rule: Default to mocktail (no codegen, faster setup). Use mockito only when you need @GenerateNiceMocks for complex APIs with many overloads.
mocktail Setup (Recommended)
import 'package:mocktail/mocktail.dart';
// Create mock — no code generation needed
class MockUserRepository extends Mock implements UserRepository {}
void main() {
late MockUserRepository mockRepo;
setUp(() {
mockRepo = MockUserRepository();
});
test('fetches user successfully', () {
// Arrange
when(() => mockRepo.getUser('1'))
.thenAnswer((_) async => const User(id: '1', name: 'Alice'));
// Act
final user = await mockRepo.getUser('1');
// Assert
expect(user.name, 'Alice');
verify(() => mockRepo.getUser('1')).called(1);
});
}
mockito Setup (Code-Gen)
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
@GenerateNiceMocks([MockSpec<UserRepository>()])
import 'user_test.mocks.dart'; // Generated by: dart run build_runner build
Verification APIs
| API | Purpose |
|---|---|
verify(() => mock.method()).called(1) |
Assert method was called exactly once |
verifyNever(() => mock.method()) |
Assert method was never called |
verifyNoMoreInteractions(mock) |
Assert no unchecked interactions remain |
verifyInOrder([...]) |
Assert methods were called in specific order |
reset(mock) |
Clear all stubs and interactions |
Registering Fallback Values
For methods with custom types as parameters:
setUpAll(() {
registerFallbackValue(const User(id: '0', name: 'fallback'));
});
Test Coverage
- Target 100% logic coverage for domain models and business logic.
- Run coverage with
dart test --coverage=coverageorflutter test --coverage. - See
dart-coverageskill for detailed LCOV/HTML report generation.
Workflow: Testing Execution
Follow this sequential workflow when testing a Dart feature. Copy the checklist to track progress.
Task Progress
- [ ] Step 1: Write Unit Tests. Focus on core business logic. Ensure files end with
_test.dart. - [ ] Step 2: Generate Mocks. Run
dart run build_runner buildto generate mocks if using Mockito. - [ ] Step 3: Run Tests. Execute
dart test. - [ ] Step 4: Check Coverage. Run
dart test --coverage=coverageand verify coverage targets are met.