1. Unit Testing
// test/product_repository_test.dart
import 'package:mocktail/mocktail.dart';
import 'package:test/test.dart';
class MockApiClient extends Mock implements ApiClient {}
void main() {
late ProductRepository repository;
late MockApiClient mockApi;
setUp(() {
mockApi = MockApiClient();
repository = ProductRepository(mockApi);
});
group('getProducts', () {
test('should return list of products on success', () async {
final products = [Product(id: '1', name: 'Laptop', price: 999)];
when(() => mockApi.getProducts()).thenAnswer((_) async => products);
final result = await repository.getProducts();
expect(result, isA<Right>());
result.fold(
(failure) => fail('Should not be failure'),
(data) => expect(data, equals(products)),
);
verify(() => mockApi.getProducts()).called(1);
});
test('should return ServerFailure on exception', () async {
when(() => mockApi.getProducts()).thenThrow(ServerException('Server error'));
final result = await repository.getProducts();
expect(result, isA<Left>());
result.fold(
(failure) => expect(failure, isA<ServerFailure>()),
(_) => fail('Should be failure'),
);
});
});
}
2. Widget Testing
// test/widgets/product_card_test.dart
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('ProductCard displays product info', (tester) async {
final product = Product(id: '1', name: 'Laptop', price: 999);
await tester.pumpWidget(
MaterialApp(
home: Scaffold(body: ProductCard(product: product)),
),
);
expect(find.text('Laptop'), findsOneWidget);
expect(find.text('\$999.00'), findsOneWidget);
});
testWidgets('ProductCard tap navigates to detail', (tester) async {
final product = Product(id: '1', name: 'Laptop', price: 999);
var tapped = false;
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: ProductCard(
product: product,
onTap: () => tapped = true,
),
),
),
);
await tester.tap(find.byType(ProductCard));
await tester.pumpAndSettle();
expect(tapped, isTrue);
});
}
3. Testing Bloc/Cubit
import 'package:bloc_test/bloc_test.dart';
class MockGetProducts extends Mock implements GetProducts {}
void main() {
late ProductBloc bloc;
late MockGetProducts mockGetProducts;
setUp(() {
mockGetProducts = MockGetProducts();
bloc = ProductBloc(getProducts: mockGetProducts);
});
blocTest<ProductBloc, ProductState>(
'emits [Loading, Loaded] when FetchProducts is added',
build: () {
when(() => mockGetProducts(any()))
.thenAnswer((_) async => Right([Product(id: '1', name: 'Laptop', price: 999)]));
return bloc;
},
act: (bloc) => bloc.add(FetchProducts()),
expect: () => [
const ProductLoading(),
isA<ProductLoaded>(),
],
);
}
4. Integration Testing
// integration_test/app_test.dart
import 'package:integration_test/integration_test.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
testWidgets('Full login flow', (tester) async {
await tester.pumpWidget(const MyApp());
await tester.pumpAndSettle();
// Nhập email
await tester.enterText(find.byKey(const Key('email_field')), '[email protected]');
// Nhập password
await tester.enterText(find.byKey(const Key('password_field')), 'password123');
// Tap login
await tester.tap(find.byKey(const Key('login_button')));
await tester.pumpAndSettle();
// Verify navigation to home
expect(find.text('Home'), findsOneWidget);
});
}
# Run tests
flutter test
flutter test --coverage
flutter test integration_test/
Next article: Performance & Debugging.