Aplikasi yang belum diuji hanyalah aplikasi yang kita harapkan benar — dan harapan tidak membantu pengguna yang CSV-nya rusak. Bagian ini menutup proyek dengan bukti: lapisan logika diuji otomatis berjalan (18 kasus, termasuk delapan kasus galat), antarmuka diuji manual mengikuti rencana tertulis, dan hasil analisis dilindungi dari regresi. Cakupannya:

  • strategi uji dua lapis: otomatis untuk logika, manual terencana untuk UI,
  • tests/uji_logika.m: pola tabel kasus, kasus galat, dan hasil lulus nyata,
  • rencana uji manual: langkah → aksi → hasil diharapkan,
  • uji regresi, packaging, checklist akhir, dan ide pengembangan.

Strategi Uji Dua Lapis #

Arsitektur dua lapis dari Bagian 1 menentukan strategi ujinya: setiap lapisan diuji dengan alat yang paling cocok untuknya. Logika murni tidak butuh jendela — cukup assert di berkas uji yang berjalan di Octave maupun MATLAB. Antarmuka sebaliknya hanya hidup di desktop MATLAB dan membutuhkan mata manusia:

LapisanAlat ujiKapan dijalankan
Logika (src/ kecuali app)tests/uji_logika.m, otomatissetiap perubahan kode
Antarmuka (aplikasiAnalisis.m)rencana uji manualsebelum rilis dan setelah ubah UI

Pembagian ini bukan kompromi karena GUI tidak bisa diuji otomatis sama sekali — MATLAB menyediakan kerangka uji app — melainkan pilihan sadar untuk proyek ini: melindungi bagian yang paling berisiko salah (hitungan dan parsing) dengan uji murah, lalu memeriksa bagian visual dengan daftar periksa yang tertulis sehingga tidak bergantung ingatan.

Uji Otomatis Lapisan Logika #

Berkas tests/uji_logika.m dibuka dengan pola addpath yang membuatnya bisa dijalankan dari folder mana pun:

matlab
% Pastikan folder src/ dapat diakses dari lokasi berkas ini,
% entah dijalankan dari tests/ maupun dari lokasi lain.
folderIni = fileparts(mfilename('fullpath'));
addpath(fullfile(folderIni, '..', 'src'));

Mari kita bedah baris-baris kode di atas:

  1. Baris 2 (fileparts(mfilename('fullpath'))): lokasi berkas uji itu sendiri — bukan folder aktif — menjadi patokan, sehingga runner bisa memanggilnya dari mana pun.
  2. Baris 3 (addpath(fullfile(...))): naik satu tingkat ke src/ memasang seluruh fungsi logika ke path.

Berikutnya, persiapan yang khas proyek GUI ini: berkas CSV uji ditulis ke folder sementara yang membersihkan dirinya sendiri, dan korelasi diverifikasi dengan rumus yang ditulis ulang — bukan memanggil fungsi yang sama dua kali:

matlab
% Folder sementara untuk berkas CSV uji, dibuang otomatis saat skrip
% selesai (termasuk saat skrip berakhir karena error).
folderUji = tempname();
mkdir(folderUji);
pembersih = onCleanup(@() rmdir(folderUji, 's'));   %#ok<NASGU>

% Korelasi Pearson dihitung ulang dari rumus untuk memverifikasi hasil
% analisisInti — bukan membandingkan output fungsi dengan dirinya sendiri.
korelasiManual = @(x, y) sum((x - mean(x)) .* (y - mean(y))) ...
    / sqrt(sum((x - mean(x)) .^ 2) * sum((y - mean(y)) .^ 2));

Tabel kasus nilainya memeriksa statistik dihitung ulang oleh test sendiri — mean, std, min, maks dari data yang dibaca, bukan angka yang diketik tangan:

matlab
kasus = { ...
    'buatDataContoh mengembalikan nama berkas', ...
        @() assert(strcmp(berkasKembali, berkasContoh)); ...
    'buatDataContoh menulis berkas di folder uji', ...
        @() assert(exist(berkasContoh, 'file') == 2); ...
    'muatCSV membaca 12 baris 3 kolom', ...
        @() assert(isequal(size(d.data), [12 3])); ...
    'muatCSV membaca nama kolom header', ...
        @() assert(isequal(d.namaKolom, {'bulan', 'penjualan', 'suhu'})); ...
    'muatCSV kolom bulan utuh 1..12', ...
        @() assert(isequal(d.data(:, 1), (1:12)')); ...
    'analisisInti mean/std/min/maks per kolom', ...
        @() assert(isequal(h.mean, mean(d.data)) ...
            && isequal(h.stdDev, std(d.data, 0, 1)) ...
            && isequal(h.minNilai, min(d.data, [], 1)) ...
            && isequal(h.maksNilai, max(d.data, [], 1))); ...
    'analisisInti jumlah baris dan kolom', ...
        @() assert(h.jumlahBaris == 12 && h.jumlahKolom == 3); ...
    'analisisInti korelasi sesuai rumus', ...
        @() assert(abs(h.korelasi(1) ...
            - korelasiManual(d.data(:, 1), d.data(:, 2))) < 1e-12 ...
            && abs(h.korelasi(2) ...
            - korelasiManual(d.data(:, 1), d.data(:, 3))) < 1e-12); ...
    'laporan memuat kata kunci', ...
        @() assert(all(cellfun(@(kata) ~isempty(strfind(h.laporan, kata)), ...
            {'Laporan', 'Mean', 'Std', 'Korelasi', 'bulan', ...
            'penjualan', 'suhu'}))); ...
    'analisisInti nama kolom bawaan', ...
        @() assert(isequal(analisisInti(struct('data', [1 2; 3 4])) ...
            .namaKolom, {'kolom1', 'kolom2'})) ...
};

Mesin pengujinya satu loop try/catch yang menghitung LULUS/GAGAL — pola yang sama dengan proyek kalkulator matriks. Kasus galatnya membalik logika: panggilan wajib melempar galat, dan pesannya harus memuat fragmen yang dijanjikan:

matlab
for k = 1:size(galat, 1)
    lulus = false;
    try
        galat{k, 2}();   % wajib melempar galat
    catch err
        lulus = ~isempty(strfind(err.message, galat{k, 3}));
    end
    if lulus
        nLulus = nLulus + 1;
        fprintf('LULUS: %s\n', galat{k, 1});
    else
        nGagal = nGagal + 1;
        fprintf('GAGAL: %s\n', galat{k, 1});
    end
end

Delapan kasus galat menutup seluruh guard yang dibangun di Bagian 3:

Kondisi cacatFragmen pesan yang diharapkan
berkas tidak adaBerkas tidak ditemukan: ...
CSV hanya satu kolomCSV memerlukan minimal 2 kolom, ditemukan 1.
token bukan angkabukan angka
jumlah kolom baris berubahkolom, header
header tanpa baris dataTidak ada baris data setelah header.
data satu kolom ke analisisIntiminimal 2 kolom
data kosongtidak boleh kosong
namaKolom salah panjangnamaKolom

Menjalankan berkas dari folder tests/ (Octave 10.3, mode batch) memberi hasil:

Output yang Dihasilkan:

text
== uji_logika.m ==
LULUS: buatDataContoh mengembalikan nama berkas
LULUS: buatDataContoh menulis berkas di folder uji
LULUS: muatCSV membaca 12 baris 3 kolom
LULUS: muatCSV membaca nama kolom header
LULUS: muatCSV kolom bulan utuh 1..12
LULUS: analisisInti mean/std/min/maks per kolom
LULUS: analisisInti jumlah baris dan kolom
LULUS: analisisInti korelasi sesuai rumus
LULUS: laporan memuat kata kunci
LULUS: analisisInti nama kolom bawaan
LULUS: muatCSV berkas tidak ada
LULUS: muatCSV CSV satu kolom
LULUS: muatCSV token bukan angka
LULUS: muatCSV jumlah kolom berubah
LULUS: muatCSV header tanpa baris data
LULUS: analisisInti data satu kolom
LULUS: analisisInti data kosong
LULUS: analisisInti namaKolom salah panjang

Ringkasan: 18 lulus, 0 gagal (total 18)
BerkasCakupanKasusHasil
uji_logika.mseluruh lapisan logika1818 lulus, 0 gagal, kode keluar 0

Kode keluar 0 penting: berkas uji sengaja melempar error hanya bila ada kegagalan, sehingga runner otomatis mendeteksi masalah tanpa membaca layar.

Rencana Uji Manual Antarmuka #

Lapisan UI diuji dengan rencana tertulis — setiap langkah punya hasil yang bisa dicek. Rencana ini dijalankan di desktop MATLAB, sekali sebelum rilis dan setiap kali UI berubah:

#AksiHasil yang diharapkan
1jalankan aplikasiAnalisisjendela 720 x 520 terbuka; dropdown pucat dan tidak bisa diklik; label menampilkan panduan
2klik Muat Data (berkas belum ada)data_contoh.csv dibuat; grafik bulan-penjualan tampil dengan penanda titik; ringkasan tiga baris; dropdown aktif
3ganti Kolom Y ke suhugrafik digambar ulang; ringkasan berganti (mean 26.9, korelasi bulan-suhu: 0.012); berkas tidak dibaca ulang
4sunting CSV dengan editor teks, klik Muat Data lagiringkasan memuat nilai baru
5rusak CSV (ganti angka dengan abc), klik Muat Datadialog galat uialert berisi Baris 1 kolom 2 ("abc") bukan angka.; aplikasi tetap hidup
6klik Muat Data dua kali dengan cepattombol terkunci selama proses; hanya satu pemuatan
7perbesar jendelagrafik ikut membesar; kontrol dan ringkasan tetap 'fit'
8navigasi dengan Tabfokus mencapai tombol dan dropdown tanpa mouse

Uji Regresi #

Refactor adalah momen paling rawan: kode berubah, hasil harus tidak. Uji regresi melindunginya dengan perbandingan nilai — isequal terhadap hasil yang diketahui benar. Contoh terkecilnya, data contoh harus selalu terbaca kembali utuh:

matlab
% regresi: data contoh harus selalu terbaca kembali utuh
buatDataContoh();
d = muatCSV('data_contoh.csv');
M = [ (1:12)', [200 215 230 255 270 290 305 320 330 345 360 380]', ...
      [26.0 26.5 25.5 27.0 28.0 28.5 28.5 27.5 27.0 26.5 26.0 26.0]' ];
fprintf('round-trip utuh: %d\n', isequal(d.data, M));

Output yang Dihasilkan:

text
round-trip utuh: 1

Prinsip kerjanya: bila suatu saat muatCSV dirombak (misalnya pindah ke textscan), satu baris isequal ini langsung melaporkan apakah hasil pembacaan masih identik. Kasus round-trip semacam ini — tulis lalu baca — adalah uji regresi termurah untuk semua fungsi I/O.

Packaging dan Distribusi #

Cara termudah membagikan aplikasi: kirim folder src/ (empat berkas .m) — data_contoh.csv tidak perlu disertakan karena dibuat ulang otomatis saat klik pertama. Penerima menjalankan aplikasiAnalisis setelah berada di folder itu.

CaraPenerima perluCocok untuk
Kirim src/lisensi MATLABsesama pengguna MATLAB
Kompilasi dengan MATLAB Compilerhanya MATLAB Runtime (gratis)pengguna tanpa MATLAB

Opsi kedua mengemas aplikasi menjadi program mandiri; penerima memasang MATLAB Runtime — pustaka gratis dari MathWorks berukuran ratusan MB hingga beberapa GB — tanpa perlu lisensi MATLAB sama sekali.

Checklist Akhir Proyek #

Audit hasil kerja terhadap janji-janji Bagian 1:

Janji Bagian 1Bukti
Muat CSV tanpa satu perintah puntombol Muat Data + callback; uji manual #1-2
Statistik dan korelasi benarkasus uji 6-8 menghitung ulang nilainya; 18/18 lulus
Grafik mengikuti pilihan kolomplot(axGrafik, ...) target eksplisit; uji manual #3
Ringkasan tiga barissprintf multi-baris; output driver nyata di Bagian 3
Galat ditolak dengan pesan jelas8 kasus galat + uialert di uji manual #5
Data hidup setelah dimuatganti Kolom Y tanpa membaca ulang berkas
Logika teruji otomatisuji_logika.m: 18 lulus, kode keluar 0
Tanpa berkas tersisaberkas uji di folder tempname + onCleanup

Ide Pengembangan Lanjutan #

Struktur dua lapis membuat pengembangan berikutnya murah — UI dan logika tumbuh tanpa saling mengganggu:

  • multi-berkas — ganti nama berkas tetap dengan dialog uigetfile() di callback Muat Data; logika muatCSV tidak berubah sama sekali,
  • export hasil — tombol Simpan yang memanggil exportgraphics(axGrafik, ...) untuk grafik dan menulis hasil.laporan ke berkas teks,
  • halaman bertab — uitabgroup memisahkan tab Grafik, Tabel, dan Ringkasan pada jendela yang sama,
  • tabel data mentah — uitable menampilkan matriks hasil muatCSV langsung di app,
  • kolom X bebas — dropdown kedua sehingga korelasi bisa dihitung pasangan kolom mana pun.

Setiap kandidat menuntut pola yang sama: sedikit kode UI di aplikasiAnalisis.m, sedikit logika murni di src/, dan beberapa baris kasus uji baru — arsitektur yang sudah terbukti.

Proyek aplikasi analisis data tuntas: antarmuka dari Bagian 2, logika teruji dari Bagian 3, dan bukti dari Bagian 4 membentuk aplikasi yang bisa dipakai orang lain tanpa satu baris kode. Cetak birunya layak dibawa ke proyek berikutnya — pisahkan logika dari tampilan, uji logika secara otomatis, rencanakan uji tampilan secara tertulis, dan biarkan galat diterima dengan sopan.