Kodax

اختبار التطبيقات — Flutter المتقدم

اختبار تطبيقات Flutter

أنواع الاختبارات

1. Unit Tests: اختبار الدوال والـ Classes

2. Widget Tests: اختبار المكونات

3. Integration Tests: اختبار التطبيق كاملاً

1. Unit Tests

// lib/services/calculator.dart
class Calculator {
  int add(int a, int b) => a + b;
  int subtract(int a, int b) => a - b;
  int multiply(int a, int b) => a * b;
  double divide(int a, int b) {
    if (b == 0) throw ArgumentError('Cannot divide by zero');
    return a / b;
  }
}

// test/calculator_test.dart
import 'package:test/test.dart';
import 'package:my_app/services/calculator.dart';

void main() {
  group('Calculator', () {
    late Calculator calculator;

    setUp(() {
      calculator = Calculator();
    });

    test('should add two numbers', () {
      expect(calculator.add(2, 3), equals(5));
    });

    test('should subtract two numbers', () {
      expect(calculator.subtract(5, 3), equals(2));
    });

    test('should multiply two numbers', () {
      expect(calculator.multiply(3, 4), equals(12));
    });

    test('should divide two numbers', () {
      expect(calculator.divide(10, 2), equals(5.0));
    });

    test('should throw error when dividing by zero', () {
      expect(
        () => calculator.divide(10, 0),
        throwsA(isA<ArgumentError>()),
      );
    });
  });
}

2. Widget Tests

// test/widgets/counter_test.dart
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:my_app/screens/counter_screen.dart';

void main() {
  testWidgets('Counter increments', (WidgetTester tester) async {
    // Build widget
    await tester.pumpWidget(MaterialApp(home: CounterScreen()));

    // Verify initial state
    expect(find.text('0'), findsOneWidget);
    expect(find.text('1'), findsNothing);

    // Tap the button
    await tester.tap(find.byIcon(Icons.add));
    await tester.pump(); // Rebuild

    // Verify state changed
    expect(find.text('0'), findsNothing);
    expect(find.text('1'), findsOneWidget);
  });

  testWidgets('Counter decrements', (WidgetTester tester) async {
    await tester.pumpWidget(MaterialApp(home: CounterScreen()));

    // Tap increment first
    await tester.tap(find.byIcon(Icons.add));
    await tester.pump();

    // Then decrement
    await tester.tap(find.byIcon(Icons.remove));
    await tester.pump();

    expect(find.text('0'), findsOneWidget);
  });
}

3. Testing with Mocks

dev_dependencies:
  mockito: ^5.4.0
  build_runner: ^2.4.0
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'api_service.dart';

@GenerateMocks([ApiService])
import 'user_repository_test.mocks.dart';

void main() {
  late MockApiService mockApi;
  late UserRepository repository;

  setUp(() {
    mockApi = MockApiService();
    repository = UserRepository(mockApi);
  });

  test('should return users from API', () async {
    // Arrange
    final users = [User(id: 1, name: 'Test')];
    when(mockApi.getUsers()).thenAnswer((_) async => users);

    // Act
    final result = await repository.getUsers();

    // Assert
    expect(result, equals(users));
    verify(mockApi.getUsers()).called(1);
  });

  test('should throw when API fails', () async {
    when(mockApi.getUsers()).thenThrow(Exception('Network error'));

    expect(
      () => repository.getUsers(),
      throwsA(isA<Exception>()),
    );
  });
}

4. تشغيل الاختبارات

# كل الاختبارات
flutter test

# ملف محدد
flutter test test/calculator_test.dart

# مع تغطية الكود
flutter test --coverage
اختبر فهمك