MIF_E31230745/tests/Feature/ExplainableRecommendationTe...

253 lines
9.1 KiB
PHP

<?php
namespace Tests\Feature;
use App\Models\User;
use App\Models\PolijeMajor;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class ExplainableRecommendationTest extends TestCase
{
use RefreshDatabase;
protected function setUp(): void
{
parent::setUp();
// Create some jurusan data
PolijeMajor::create([
'nama_jurusan' => 'Teknologi Informasi',
'singkatan' => 'TI',
'tujuan_kompetensi' => 'Menghasilkan profesional IT',
'prospek_kerja' => 'Software Developer, Software Engineer, IT Consultant',
'kelompok_asal' => 'IPA',
'mtk' => 30, 'fisika' => 20, 'kimia' => 10, 'biologi' => 5,
'minat1' => 'Logika Komputer', 'minat2' => '', 'minat3' => '',
'pref_sains' => 70, 'pref_pertanian' => 20, 'pref_kesehatan' => 10,
'pref_bisnis' => 15, 'pref_sosial' => 10, 'pref_praktik' => 50, 'pref_teori' => 50,
]);
PolijeMajor::create([
'nama_jurusan' => 'Akuntansi',
'singkatan' => 'AK',
'tujuan_kompetensi' => 'Menghasilkan profesional akuntansi',
'prospek_kerja' => 'Accountant, Financial Analyst, Tax Consultant',
'kelompok_asal' => 'IPS',
'ekonomi' => 30, 'geografi' => 15, 'sosiologi' => 10, 'sejarah' => 5,
'minat1' => 'Bisnis', 'minat2' => '', 'minat3' => '',
'pref_bisnis' => 80, 'pref_sosial' => 15, 'pref_pertanian' => 5,
'pref_sains' => 10, 'pref_kesehatan' => 5, 'pref_praktik' => 60, 'pref_teori' => 40,
]);
}
/**
* Test bahwa response recommendation mencakup explanation
*/
public function test_recommendation_includes_explanation()
{
$user = User::factory()->create([
'role' => 'siswa',
'kelompok_asal' => 'IPA',
]);
$this->actingAs($user)->post(route('rekomendasi.proses'), [
'mtk' => 90,
'fisika' => 85,
'kimia' => 80,
'biologi' => 75,
'minat' => 'Logika Komputer',
'pref_studi' => 'Sains & Teknologi',
'cita_cita' => 'Software Developer',
'prestasi' => 'Juara Kompetisi Coding Nasional',
]);
// Verify recommendation is stored
$lastRecommendation = \App\Models\Recommendation::latest()->first();
$this->assertNotNull($lastRecommendation);
// Verify hasil_rekomendasi contains explanation
$hasil = $lastRecommendation->hasil_rekomendasi;
$this->assertIsArray($hasil);
$this->assertNotEmpty($hasil);
// Check top recommendation has explanation field
$topRec = $hasil[0];
$this->assertArrayHasKey('explanation', $topRec);
$this->assertIsArray($topRec['explanation']);
// Verify all 5 explanation keys exist
$expectedKeys = ['nilai', 'minat', 'pref', 'cita', 'prestasi'];
foreach ($expectedKeys as $key) {
$this->assertArrayHasKey($key, $topRec['explanation']);
$this->assertIsString($topRec['explanation'][$key]);
$this->assertNotEmpty($topRec['explanation'][$key]);
}
}
/**
* Test bahwa explanation berisi teks yang meaningful
*/
public function test_explanation_contains_meaningful_text()
{
$user = User::factory()->create([
'role' => 'siswa',
'kelompok_asal' => 'IPA',
]);
$this->actingAs($user)->post(route('rekomendasi.proses'), [
'mtk' => 95,
'fisika' => 90,
'kimia' => 88,
'biologi' => 92,
'minat' => 'Logika Komputer',
'pref_studi' => 'Sains & Teknologi',
'cita_cita' => 'Software Engineer',
'prestasi' => 'Juara Olimpiade Komputer',
]);
$lastRecommendation = \App\Models\Recommendation::latest()->first();
$hasil = $lastRecommendation->hasil_rekomendasi;
$topRec = $hasil[0];
$explanation = $topRec['explanation'];
// Verify explanation contains meaningful indicators and text
$this->assertStringContainsString('Nilai akademik', $explanation['nilai']);
$this->assertStringContainsString('Minat', $explanation['minat']);
$this->assertStringContainsString('pembelajaran', $explanation['pref']);
$this->assertStringContainsString('cita', $explanation['cita']);
$this->assertStringContainsString('prestasi', strtolower($explanation['prestasi']));
// Verify checkmarks or indicators are present
$combinedExplanation = implode(' ', $explanation);
$this->assertTrue(
str_contains($combinedExplanation, '✅') ||
str_contains($combinedExplanation, '✓') ||
str_contains($combinedExplanation, 'sesuai') ||
str_contains($combinedExplanation, 'cocok')
);
}
/**
* Test scoring detail (nilai, minat, pref, cita, prestasi) tersimpan dengan tepat
*/
public function test_scoring_detail_stored_correctly()
{
$user = User::factory()->create([
'role' => 'siswa',
'kelompok_asal' => 'IPA',
]);
$this->actingAs($user)->post(route('rekomendasi.proses'), [
'mtk' => 88,
'fisika' => 82,
'kimia' => 85,
'biologi' => 80,
'minat' => 'Logika Komputer dan Pemrograman',
'pref_studi' => 'Sains & Teknologi',
'cita_cita' => 'Software Engineer',
'prestasi' => 'Sertifikat Oracle Java',
]);
$lastRecommendation = \App\Models\Recommendation::latest()->first();
$hasil = $lastRecommendation->hasil_rekomendasi;
// Verify detail breakdown exists for top recommendation
$topRec = $hasil[0];
$this->assertArrayHasKey('detail', $topRec);
$detail = $topRec['detail'];
// Verify all 5 scoring components exist
$scores = ['nilai', 'minat', 'pref', 'cita', 'prestasi'];
foreach ($scores as $score) {
$this->assertArrayHasKey($score, $detail);
$this->assertIsNumeric($detail[$score]);
$this->assertGreaterThanOrEqual(0, $detail[$score]);
$this->assertLessThanOrEqual(1, $detail[$score]);
}
}
/**
* Test bahwa setiap jurusan dalam hasil punya explanation
*/
public function test_all_recommendations_have_explanations()
{
$user = User::factory()->create([
'role' => 'siswa',
'kelompok_asal' => 'IPA',
]);
$this->actingAs($user)->post(route('rekomendasi.proses'), [
'mtk' => 80,
'fisika' => 75,
'kimia' => 78,
'biologi' => 76,
'minat' => 'Sains dan Teknologi',
'pref_studi' => 'Sains & Teknologi',
'cita_cita' => 'Profesional Teknologi',
'prestasi' => 'Aktif dalam kegiatan STEM',
]);
$lastRecommendation = \App\Models\Recommendation::latest()->first();
$hasil = $lastRecommendation->hasil_rekomendasi;
// Verify each recommendation has explanation and detail
foreach ($hasil as $rec) {
$this->assertArrayHasKey('explanation', $rec);
$this->assertArrayHasKey('detail', $rec);
$this->assertIsArray($rec['explanation']);
$this->assertIsArray($rec['detail']);
// Count should match (5 criteria)
$this->assertCount(5, $rec['explanation']);
$this->assertCount(5, $rec['detail']);
}
}
/**
* Test explanation rendered in view
*/
public function test_explanation_displayed_in_view()
{
$user = User::factory()->create([
'role' => 'siswa',
'kelompok_asal' => 'IPA',
]);
$response = $this->actingAs($user)->post(route('rekomendasi.proses'), [
'mtk' => 85,
'fisika' => 80,
'kimia' => 78,
'biologi' => 82,
'minat' => 'Logika Komputer',
'pref_studi' => 'Sains & Teknologi',
'cita_cita' => 'Software Developer',
'prestasi' => 'Juara Informatika',
]);
$response->assertStatus(200);
// View should contain explanation indicators
$response->assertViewHas('hasilAkhir');
$hasil = $response->viewData('hasilAkhir');
// Check explanation is in view data
$this->assertNotEmpty($hasil);
$topRec = $hasil[0];
$this->assertArrayHasKey('explanation', $topRec);
$explanation = $topRec['explanation'];
// Verify explanation content in response
foreach ($explanation as $key => $text) {
$this->assertNotEmpty($text);
// Check that text contains expected keywords
$hasContent = str_contains($text, '✅') ||
str_contains($text, '✓') ||
str_contains($text, 'sesuai') ||
str_contains($text, 'cocok') ||
str_contains($text, 'relevan');
$this->assertTrue($hasContent, "Explanation for $key should have meaningful content");
}
}
}