mogen_unit_tests 1.0.4
mogen_unit_tests: ^1.0.4 copied to clipboard
Scans Riverpod AsyncNotifier classes and auto-generates Mocktail unit tests.
example/example.dart
// ignore_for_file: avoid_print
/// This file demonstrates how mogen_unit_tests works end-to-end.
///
/// Given the following feature structure in your Flutter project:
///
/// ```
/// lib/
/// └── features/
/// └── cart/
/// └── presentation/
/// ├── notifiers/
/// │ └── cart_notifier.dart
/// └── states/
/// └── cart_state.dart
/// ```
///
/// Running `dart run mogen_unit_tests` from your project root generates:
///
/// ```
/// test/
/// └── unit/
/// └── features/
/// └── cart/
/// └── cart_notifier_test.dart
///
/// ```
///
/// ---
///
/// ## Example input — cart_notifier.dart
///
/// ```dart
/// import 'package:riverpod_annotation/riverpod_annotation.dart';
/// import '../../states/cart_state.dart';
/// import '../../../data/repositories/cart_repository_impl.dart';
///
/// part 'cart_notifier.g.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> removeItem(String itemId) async {
/// final repo = ref.read(cartRepositoryProvider);
/// await repo.removeItem(itemId);
/// ref.invalidateSelf();
/// }
/// }
/// ```
///
/// ## Example output — cart_notifier_test.dart
///
/// ```dart
/// // GENERATED BY mogen_unit_tests — DO NOT EDIT
/// // ignore_for_file: lines_longer_than_80_chars
///
/// 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';
/// import 'package:my_app/features/cart/data/repositories/cart_repository_impl.dart';
///
/// // ── 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.fetchCart())
/// // .thenAnswer((_) async => CartState(items: [], total: 0.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((_) {});
///
/// expect(container.read(cartNotifierProvider), isA<AsyncError>());
/// });
/// });
///
/// // ── addItem() ────────────────────────────────────────────────────────
/// group('addItem', () {
/// test('addItem completes successfully', () async {
/// final item = FakeCartItem();
///
/// await container.read(cartNotifierProvider.future);
/// await container.read(cartNotifierProvider.notifier).addItem(item);
///
/// // verify(() => mockCartRepository.addItem(any())).called(1);
/// });
///
/// test('addItem handles error from repository', () async {
/// // when(() => mockCartRepository.addItem(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>()),
/// );
/// });
/// });
///
/// // ── removeItem() ─────────────────────────────────────────────────────
/// group('removeItem', () {
/// test('removeItem completes successfully', () async {
/// final itemId = '';
///
/// await container.read(cartNotifierProvider.future);
/// await container
/// .read(cartNotifierProvider.notifier)
/// .removeItem(itemId);
/// });
///
/// test('removeItem handles error from repository', () async {
/// final itemId = '';
///
/// await container.read(cartNotifierProvider.future).catchError((_) {});
///
/// expect(
/// () async => container
/// .read(cartNotifierProvider.notifier)
/// .removeItem(itemId),
/// throwsA(isA<Exception>()),
/// );
/// });
/// });
/// });
/// }
/// ```
library;
// After installing, run from your Flutter project root:
//
// dart run mogen_unit_tests
//
// Options:
// --dry-run Preview output without writing files
// --verbose Print detailed progress
// --root Path to project root (defaults to current directory)
void main() {
print('Run `dart run mogen_unit_tests` from your Flutter project root.');
}