dart dart
npx skills add dhruvanbhalara/skills --skill dart-add-unit-test

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 lib directory structure. Append _test.dart to test files.
  • Group related tests using the group() function.
  • Use setUp() and tearDown() 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=coverage or flutter test --coverage.
  • See dart-coverage skill 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 build to generate mocks if using Mockito.
  • [ ] Step 3: Run Tests. Execute dart test.
  • [ ] Step 4: Check Coverage. Run dart test --coverage=coverage and verify coverage targets are met.