mogen_unit_tests

mogen_unit_tests scans your Flutter feature folders, parses Riverpod notifiers, and generates Mocktail-based unit test scaffolding for each discovered notifier.

It is designed for projects that keep their notifier and state files under lib/features/**/presentation/ and want a fast starting point for repeatable unit tests.


What it generates

For every notifier it finds, the package creates a ready-to-run test file with:

  • Mock<Repository> classes for every repository dependency found via ref.read(...) or ref.watch(...)
  • Fake<Type> classes and registerFallbackValue(...) calls for complex parameter types
  • a ProviderContainer with repository overrides in setUp
  • container.dispose() in tearDown
  • a build() test group with success and error cases
  • one test group per public notifier method, each with success and error cases

Project layout

The CLI expects this layout inside your Flutter app:

lib/
└── features/
    └── cart/
        └── presentation/
            ├── notifiers/
            │   └── cart_notifier.dart
            └── states/
                └── cart_state.dart

Generated tests are written to:

test/
└── features/
    └── unit/
        └── features/
            └── cart/
                └── cart_notifier_test.dart

The generated path is test/unit/features/<feature>/<notifier>_test.dart.


Installation

Add the package to your Flutter app's dev_dependencies:

dev_dependencies:
    mogen_unit_tests:

Install it:

dart pub get

Usage

Run the generator from the root of your Flutter app:

dart run mogen_unit_tests

CLI options

Flag Short Default Description
--root -r . Project root directory
--dry-run -d false Preview generated files without writing them
--verbose -v false Print progress details while scanning and generating
--version Print the package version and exit
--help -h Show the CLI help text

Examples

# Generate tests from the current directory
 dart run mogen_unit_tests

# Preview the output without writing files
 dart run mogen_unit_tests --dry-run --verbose

# Run against a different Flutter project
 dart run mogen_unit_tests --root /path/to/my_flutter_app

Example

Input notifier

@riverpod
class CartNotifier extends _$CartNotifier {
  @override
  Future<CartState> build() async {
    final repo = ref.read(cartRepositoryProvider);
    return repo.fetchCart();
  }

  Future<void> addItem(CartItem item) async {
    final repo = ref.read(cartRepositoryProvider);
    await repo.addItem(item);
    ref.invalidateSelf();
  }

  Future<void> clearCart() async {
    final repo = ref.read(cartRepositoryProvider);
    await repo.clearCart();
    ref.invalidateSelf();
  }
}

Input state

class CartState {
  final List<CartItem> items;
  final double total;

  const CartState({required this.items, required this.total});
}

Generated output

// GENERATED BY mogen_unit_tests — DO NOT EDIT

import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';

import 'package:my_app/features/cart/presentation/notifiers/cart_notifier.dart';
import 'package:my_app/features/cart/presentation/states/cart_state.dart';
// TODO: import the file that declares CartRepository

class MockCartRepository extends Mock implements CartRepository {}

class FakeCartItem extends Fake implements CartItem {}

void main() {
  group('CartNotifier', () {
    late MockCartRepository mockCartRepository;
    late ProviderContainer container;

    setUpAll(() {
      registerFallbackValue(FakeCartItem());
    });

    setUp(() {
      mockCartRepository = MockCartRepository();

      container = ProviderContainer(
        overrides: [
          cartRepositoryProvider.overrideWithValue(mockCartRepository),
        ],
      );
    });

    tearDown(() {
      container.dispose();
    });

    group('build', () {
      test('returns CartState successfully', () async {
        // when(() => mockCartRepository.fetchCart())
        //     .thenAnswer((_) async => CartState(items: [], total: 0));

        final result = await container.read(cartNotifierProvider.future);

        expect(result, isA<CartState>());
      });

      test('emits error when repository throws', () async {
        // when(() => mockCartRepository.fetchCart())
        //     .thenThrow(Exception('test error'));

        await container
            .read(cartNotifierProvider.future)
            .catchError((_) => FakeCartState());

        expect(container.read(cartNotifierProvider), isA<AsyncError>());
      });
    });
  });
}

What you still need to fill in

The generator scaffolds the repository stubs as comments so you can replace them with your real method names and return values.

// when(() => mockCartRepository.fetchCart())
//     .thenAnswer((_) async => CartState(items: [], total: 0));

Everything else is generated for you.


Supported notifier types

Class Supported
AsyncNotifier<T>
AutoDisposeAsyncNotifier<T>
Notifier<T>
AutoDisposeNotifier<T>

Repository dependencies are detected from ref.read(...) and ref.watch(...) calls inside the notifier implementation.


Limitations

  • Generates unit tests only — no widget or integration tests
  • Repository stubs are scaffolded as comments and must be filled in manually
  • Only notifiers under presentation/notifiers/ are scanned
  • family notifiers are not supported yet

Dependencies

Package Purpose
analyzer AST parsing of notifier source files
dart_style Formatting generated Dart output
args CLI flag parsing
glob Discovering feature folders
path Cross-platform path handling
yaml Reading package name from pubspec.yaml

Project health notes

  • This package is a CLI tool, not a Flutter app. Running dart run mogen_unit_tests from this repository itself will not produce output unless you point it at a consuming Flutter app with lib/features/....
  • The current output destination is test/unit/features/..., which is now reflected in the examples above.

Libraries

mogen_unit_tests
CLI tool that scans lib/features/**/presentation/notifiers/, parses Riverpod AsyncNotifier classes, detects repository dependencies via ref.read(), and generates Mocktail-based unit tests with success and error cases for every method.