Flutter依赖注入与状态管理插件riverpod_test的使用

发布于 1周前 作者 sinazl 来自 Flutter

Flutter依赖注入与状态管理插件riverpod_test的使用

Flutter应用中,良好的依赖注入和状态管理是构建高效、可维护应用程序的关键。riverpod_test 插件为Riverpod提供了一套测试工具,可以帮助开发者更容易地编写单元测试,确保状态管理和依赖注入逻辑的正确性。

Package

riverpod_testfelangelbloc_test 端口,针对 Riverpod Providers 进行了修改。它帮助提高代码覆盖率,通过测试所有 Riverpod Providers 来确保你的应用逻辑无误。

Pub Star on Github Flutter Website License: MIT Bloc Library

Installation

对于Flutter项目:

flutter pub add --dev riverpod_test

对于Dart项目:

dart pub add --dev riverpod_test

Usage

testProvider

用于测试简单的provider。

import 'package:riverpod_test.dart';

testProvider(
  'expect [0]',
  provider: counterProvider,
  expect: () => [0],
);

final counterProvider = Provider<int>((ref) => 0);

testNotifier

用于测试使用Notifier的状态管理器。

testNotifier(
  'expect [2] when increment is called twice and skip: 1',
  provider: counterNotifierProvider,
  act: (notifier) => notifier
      ..increment()
      ..increment(),
  skip: 1,
  expect: () => [2],
);

final counterNotifierProvider =
    NotifierProvider<CounterNotifier, int>(CounterNotifier.new);

class CounterNotifier extends Notifier<int> {
  @override
  int build() => 0;

  void increment() => state++;
}

testAsyncNotifier

用于测试异步状态管理器。

testAsyncNotifier<CounterAsyncNotifier, int>(
  'expect [AsyncData(1)] when call increment',
  provider: counterAsyncNotifierProvider(0),
  act: (notifier) => notifier.increment(),
  expect: () => <AsyncValue<int>>[const AsyncData(1)],
);

final counterAsyncNotifierProvider =
    AsyncNotifierProviderFamily<CounterAsyncNotifier, int, int>(
  CounterAsyncNotifier.new,
);

class CounterAsyncNotifier extends FamilyAsyncNotifier<int, int> {
  @override
  FutureOr<int> build(int initialValue) => initialValue;

  void increment() => state = AsyncData(value + 1);
}

testResultProvider

用于测试返回结果类型的provider。

testResultProvider<Repository>(
  'expect [1] when incrementCounter is called',
  provider: repositoryProvider,
  act: (result) => result.incrementCounter(),
  expect: () => [1],
);

final repositoryProvider = Provider<Repository>((ref) => Repository());

class Repository {
  int incrementCounter() => 1;
}

testStateNotifier

用于测试使用StateNotifier的状态管理器。

testStateNotifier<CounterStateNotifier, int>(
  'expect [0] when decrement is called and seed: 1',
  provider: counterStateNotifierProvider,
  act: (notifier) => notifier.decrement(),
  seed: 1,
  wait: const Duration(milliseconds: 100),
  expect: () => <int>[0],
);

final counterStateNotifierProvider =
    StateNotifierProvider<CounterStateNotifier, int>(
  (ref) => CounterStateNotifier(),
);

class CounterStateNotifier extends StateNotifier<int> {
  CounterStateNotifier() : super(0);

  void increment() => state++;

  Future<void> decrement() async {
    await Future<void>.delayed(const Duration(milliseconds: 100));
    state--;
  }
}

示例代码

以下是一个完整的示例demo,展示了如何在实际项目中使用riverpod_test进行测试。

import 'dart:async';
import 'package:mocktail/mocktail.dart';
import 'package:riverpod/riverpod.dart';
import 'package:riverpod_test/riverpod_test.dart';
import 'package:test/test.dart';

class MockRepository extends Mock implements Repository {}

void main() {
  mainProvider();
  mainNotifier();
  mainResultProvider();
  mainStateNotifier();
}

void mainProvider() {
  group('counterProvider', () {
    testProvider<int>(
      'expect [0]',
      provider: counterProvider,
      expect: () => const <int>[0],
    );
  });

  group('counterRepositoryProvider', () {
    final mockRepository = MockRepository();

    testProvider<int>(
      'expect [5] from repository',
      overrides: [repositoryProvider.overrideWithValue(mockRepository)],
      setUp: () => when(mockRepository.incrementCounter).thenReturn(5),
      provider: counterRepositoryProvider,
      expect: () => const <int>[5],
    );
  });
}

void mainNotifier() {
  group('counterNotifierProvider', () {
    testNotifier<CounterNotifier, int>(
      'expect [1] when increment is called',
      provider: counterNotifierProvider,
      act: (notifier) => notifier.increment(),
      expect: () => const <int>[1],
    );

    testAsyncNotifier(
      'expect [AsyncData(2)] when increment is called with seed: AsyncData(1)',
      provider: counterAsyncNotifierProvider,
      seed: const AsyncData(1),
      act: (notifier) => notifier.increment(),
      expect: () => [const AsyncData(2)],
    );
  });
}

void mainResultProvider() {
  testResultProvider<Repository>(
    'expect [1] when incrementCounter is called',
    provider: repositoryProvider,
    act: (result) => result.incrementCounter(),
    expect: () => [1],
  );
}

void mainStateNotifier() {
  testStateNotifier(
    'expect [1, 2] when increment is called twice',
    provider: counterStateNotifierProvider,
    act: (notifier) => notifier
      ..increment()
      ..increment(),
    expect: () => [1, 2],
  );
}

final counterProvider = Provider<int>((ref) => 0);

final counterRepositoryProvider =
    Provider<int>((ref) => ref.watch(repositoryProvider).incrementCounter());

final counterNotifierProvider =
    NotifierProvider<CounterNotifier, int>(CounterNotifier.new);

final counterAsyncNotifierProvider =
    AsyncNotifierProvider<CounterAsyncNotifier, int>(CounterAsyncNotifier.new);

final repositoryProvider = Provider<Repository>((ref) => Repository());

final counterStateNotifierProvider =
    StateNotifierProvider<CounterStateNotifier, int>(
  (ref) => CounterStateNotifier(),
);

class CounterNotifier extends Notifier<int> {
  @override
  int build() => 0;

  void increment() => state++;
}

class CounterAsyncNotifier extends AsyncNotifier<int> {
  @override
  FutureOr<int> build() => 0;

  void increment() => state = AsyncData(state.value! + 1);
}

class Repository {
  int incrementCounter() => 1;
}

class CounterStateNotifier extends StateNotifier<int> {
  CounterStateNotifier() : super(0);

  void increment() => state++;
}

通过上述内容,您可以了解如何在Flutter项目中使用riverpod_test进行依赖注入和状态管理的测试。希望这些信息对您有所帮助!


更多关于Flutter依赖注入与状态管理插件riverpod_test的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter依赖注入与状态管理插件riverpod_test的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在Flutter中,riverpod 是一个强大的依赖注入和状态管理库,它提供了比 provider 更简洁和更强大的API。riverpod_test 是用于测试 riverpod 提供者的一个辅助包。以下是如何在Flutter项目中使用 riverpodriverpod_test 的代码示例。

设置项目

首先,确保你的 pubspec.yaml 文件中包含了 flutter_riverpodriverpod_test 的依赖:

dependencies:
  flutter:
    sdk: flutter
  flutter_riverpod: ^x.y.z  # 替换为最新版本号

dev_dependencies:
  flutter_test:
    sdk: flutter
  riverpod_test: ^x.y.z  # 替换为最新版本号

然后运行 flutter pub get 来安装依赖。

使用 Riverpod 进行依赖注入和状态管理

以下是一个简单的例子,展示如何使用 Riverpod 来管理一个计数器状态:

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

final counterProvider = StateNotifierProvider<CounterNotifier, int>((ref) {
  return CounterNotifier();
});

class CounterNotifier extends StateNotifier<int> {
  CounterNotifier() : super(0);

  void increment() {
    state++;
  }
}

void main() {
  runApp(
    ProviderScope(
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Riverpod Example'),
        ),
        body: Center(
          child: Consumer(
            builder: (context, watch, child) {
              final count = watch(counterProvider);
              return Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: <Widget>[
                  Text(
                    'You have pushed the button this many times:',
                  ),
                  Text(
                    '$count',
                    style: Theme.of(context).textTheme.headline4,
                  ),
                ],
              );
            },
          ),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () {
            context.read(counterProvider.notifier).increment();
          },
          tooltip: 'Increment',
          child: Icon(Icons.add),
        ),
      ),
    );
  }
}

使用 Riverpod_test 进行测试

以下是如何使用 riverpod_test 来测试上面的计数器逻辑:

import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:riverpod_test/riverpod_test.dart';
import 'main.dart'; // 假设上面的代码在 main.dart 中

void main() {
  testWidgets('increment counter', (WidgetTester tester) async {
    // 使用 TestProviderScope 替代 ProviderScope
    await tester.pumpWidget(
      TestProviderScope(
        overrides: [
          counterProvider.overrideWithValue(StateNotifierProvider.autoDispose<CounterNotifier, int>((ref) => CounterNotifier())),
        ],
        child: MaterialApp(
          home: Scaffold(
            body: Consumer(
              builder: (context, watch, child) {
                final count = watch(counterProvider);
                return Text('$count');
              },
            ),
            floatingActionButton: FloatingActionButton(
              onPressed: () {
                context.read(counterProvider.notifier).increment();
              },
              child: Icon(Icons.add),
            ),
          ),
        ),
      ),
    );

    // 初始计数应该为 0
    expect(find.text('0'), findsOneWidget);

    // 点击按钮
    await tester.tap(find.byIcon(Icons.add));
    await tester.pumpAndSettle();

    // 计数应该增加到 1
    expect(find.text('1'), findsOneWidget);
  });
}

在这个测试例子中,我们使用了 TestProviderScope 来替代 ProviderScope,并且使用 overrides 参数来提供测试用的提供者实例。这样我们可以确保测试的隔离性,并且不依赖于全局状态。

这个代码展示了如何在Flutter项目中使用 riverpod 进行依赖注入和状态管理,并使用 riverpod_test 进行测试。希望这对你有所帮助!

回到顶部