BAB 57. TESTING STRATEGY
Tujuan
Menjamin kualitas sistem dan mencegah regresi sebelum kode masuk ke production melalui pendekatan Test Pyramid.
Piramida Testing
flowchart TD
E2E["🔺 E2E Test\n(Sedikit, Lambat, Mahal)"]
FEAT["🔷 Feature / Integration Test"]
UNIT["🔹 Unit Test\n(Banyak, Cepat, Murah)"]
E2E --> FEAT --> UNIT
Semakin bawah piramida = semakin banyak, cepat, dan murah.
Backend Testing — Laravel
Unit Test
Menguji logika terisolasi tanpa database.
| Target | Contoh |
|---|---|
| AI Scoring Engine | test_skill_similarity_returns_100_for_exact_match |
| SIS Calculation | test_sis_score_increases_after_successful_session |
| XP Calculation | test_xp_awarded_based_on_duration_and_rating |
| Repository Mock | test_create_request_calls_repository_once |
// tests/Unit/AI/ScoringEngineTest.php
class ScoringEngineTest extends TestCase
{
public function test_skill_similarity_exact_match_returns_100(): void
{
$engine = new ScoringEngine();
$score = $engine->calculateSkillSimilarity(
tutorSkillId: 5,
requestSubjectId: 5
);
$this->assertEquals(100, $score);
}
}
Feature Test
Menguji endpoint API secara end-to-end dengan database test.
| Endpoint | Test Case |
|---|---|
POST /auth/login | Login berhasil, password salah, akun nonaktif |
POST /requests | Request dibuat, validasi gagal, unauthorized |
POST /bookings/{id}/accept | Accept berhasil, sudah accepted, not found |
POST /meetings/check-in | QR valid, QR expired, diluar radius |
POST /ratings | Rating disimpan, duplicate rating dicegah |
// tests/Feature/Api/RequestTest.php
class RequestTest extends TestCase
{
use RefreshDatabase;
public function test_student_can_create_request(): void
{
$student = Student::factory()->create();
$response = $this->actingAs($student->user, 'sanctum')
->postJson('/api/v1/requests', [
'subject_id' => 1,
'description' => 'Butuh bantuan integral',
'preferred_location' => 'Perpustakaan',
'preferred_time' => now()->addDay(),
'duration_minutes' => 60,
]);
$response->assertCreated()
->assertJsonPath('data.status', 'searching');
}
}
Flutter Testing
Widget Test
Menguji komponen UI secara terisolasi.
// test/widget/login_form_test.dart
void main() {
testWidgets('Login form shows error when fields empty', (tester) async {
await tester.pumpWidget(const MaterialApp(home: LoginPage()));
await tester.tap(find.byKey(const Key('btn_login')));
await tester.pump();
expect(find.text('Username wajib diisi'), findsOneWidget);
});
}
Integration Test
Menguji alur pengguna dari login hingga request.
// integration_test/request_flow_test.dart
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
testWidgets('Student can login and create request', (tester) async {
app.main();
await tester.pumpAndSettle();
// Login
await tester.enterText(find.byKey(const Key('field_username')), '1234567890');
await tester.enterText(find.byKey(const Key('field_password')), 'password');
await tester.tap(find.byKey(const Key('btn_login')));
await tester.pumpAndSettle();
expect(find.text('Dashboard'), findsOneWidget);
});
}
Coverage Target
| Layer | Target Coverage |
|---|---|
| Domain / Use Cases | 90% |
| Service Layer | 85% |
| Repository | 80% |
| Controller | 75% |
| Widget | 70% |
| Overall Minimum | 80% |
Perintah Testing
# Backend — semua test
php artisan test
# Backend — dengan coverage
php artisan test --coverage --min=80
# Backend — parallel
php artisan test --parallel
# Flutter — unit & widget
flutter test
# Flutter — integration
flutter test integration_test/