mogen_unit_tests 1.0.8 copy "mogen_unit_tests: ^1.0.8" to clipboard
mogen_unit_tests: ^1.0.8 copied to clipboard

Scans Riverpod AsyncNotifier classes and auto-generates Mocktail unit tests.

mogen_unit_tests #

A CLI tool that scans your Flutter feature folders, reads your Riverpod AsyncNotifier / Notifier classes, detects repository dependencies, and auto-generates Mocktail-based unit tests with success and error cases for every method.


What it generates #

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

  • Mock<Repository> classes for every detected dependency
  • Fake<Type> classes + registerFallbackValue for every complex parameter type
  • A ProviderContainer with repository provider overrides in setUp
  • container.dispose() in tearDown
  • A build() group with a success test and an error test
  • A group per public method, each with a success test and an error test

Expected folder structure #

The tool expects this layout inside your Flutter project:

lib/
└── features/
    └── cart/                          ← feature name (any name)
        └── presentation/
            ├── notifiers/
            │   └── cart_notifier.dart ← your AsyncNotifier lives here
            └── states/
                └── cart_state.dart    ← optional, used to infer field types

Generated tests are written to:

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


Installation #

Add to your Flutter project's pubspec.yaml under dev_dependencies:

dev_dependencies:
    mogen_unit_tests: ^1.0.8

Then run:

dart pub get

Usage #

From the root of your Flutter project:

dart run mogen_unit_tests

Options #

Flag Short Default Description
--root -r . Project root directory
--dry-run -d false Preview what would be generated without writing files
--verbose -v false Print detailed progress
--version Print version and exit
--help -h Show help

Examples #

# Normal run from project root
dart run mogen_unit_tests

# Preview without writing any files
dart run mogen_unit_tests --dry-run --verbose

# Run from a different directory
dart run mogen_unit_tests --root /path/to/my_project

Example input and output #

Your notifier (lib/features/cart/presentation/notifiers/cart_notifier.dart) #

@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();
  }
}

Your state (lib/features/cart/presentation/states/cart_state.dart) #

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

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

Generated test (test/features/cart/presentation/notifiers/cart_notifier_test.dart) #

// 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

// ── Mocks ─────────────────────────────────────────────────────────────────
class MockCartRepository extends Mock implements CartRepository {}

// ── Fakes (registerFallbackValue) ─────────────────────────────────────────
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();
    });

    // ── build() ─────────────────────────────────────────────────────────
    group('build', () {
      test('returns CartState successfully', () async {
        // Arrange: stub repositories
        // when(() => mockCartRepository.someMethod(any()))
        //     .thenAnswer((_) async => CartState(...));

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

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

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

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

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

    // ── addItem() ────────────────────────────────────────────────────────
    group('addItem', () {
      test('addItem completes successfully', () async {
        // Arrange: inputs
        final item = FakeCartItem();

        await container.read(cartNotifierProvider.future);

        // Act
        await container.read(cartNotifierProvider.notifier).addItem(item);

        // Assert
        // verify(() => mockCartRepository.someMethod(any())).called(1);
      });

      test('addItem handles error from repository', () async {
        // when(() => mockCartRepository.someMethod(any()))
        //     .thenThrow(Exception('test error'));
        final item = FakeCartItem();

        await container.read(cartNotifierProvider.future).catchError((_) {});

        expect(
          () async => container.read(cartNotifierProvider.notifier).addItem(item),
          throwsA(isA<Exception>()),
        );
      });
    });

    // ── clearCart() ──────────────────────────────────────────────────────
    group('clearCart', () {
      test('clearCart completes successfully', () async {
        await container.read(cartNotifierProvider.future);

        await container.read(cartNotifierProvider.notifier).clearCart();

        // verify(() => mockCartRepository.someMethod(any())).called(1);
      });

      test('clearCart handles error from repository', () async {
        // when(() => mockCartRepository.someMethod(any()))
        //     .thenThrow(Exception('test error'));

        await container.read(cartNotifierProvider.future).catchError((_) {});

        expect(
          () async => container.read(cartNotifierProvider.notifier).clearCart(),
          throwsA(isA<Exception>()),
        );
      });
    });
  });
}

After generation #

The only lines you need to fill in manually are the when() stubs, since the tool reads your notifier but not your repository interface. Each stub is clearly marked:

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

Uncomment and replace someMethod with the real method name from your repository. Everything else runs as-is.


Supported notifier types #

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

Repository dependencies are detected via ref.read(someRepositoryProvider) and ref.watch(someRepositoryProvider) calls inside the notifier body.


Limitations #

  • Only generates unit tests — no widget or integration tests
  • Repository when() stubs are scaffolded as comments; method names must be filled in manually
  • Requires notifiers to live in presentation/notifiers/ — other paths are ignored
  • Does not support family notifiers with arguments yet

Dependencies #

Package Used for
analyzer Dart AST parsing (no build runner needed)
dart_style Formatting generated test files
args CLI flag parsing
glob Recursive file discovery
path Cross-platform path manipulation
yaml Reading pubspec.yaml for package name
1
likes
0
points
250
downloads

Publisher

unverified uploader

Weekly Downloads

Scans Riverpod AsyncNotifier classes and auto-generates Mocktail unit tests.

Homepage
Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

analyzer, args, dart_style, glob, path, yaml

More

Packages that depend on mogen_unit_tests