Pengujian unit (unit test) adalah prosedur otomatis yang memeriksa potongan kode terkecil, biasanya satu fungsi atau satu kelas, tanpa menyentuh antarmuka. Topik ini membahas pembuatan proyek uji xUnit dalam VB.NET, kebiasaan menguji lebih dulu (test-first) dengan tahap merah dan hijau, serta penggunaan atribut <Fact> dan <Theory> beserta metode kelas Assert. Kelas yang diuji adalah KalkulatorNilai yang memuat fungsi rata-rata dan konversi nilai angka menjadi huruf predikat.
Logika yang dapat diuji secara terpisah#
Sebuah pengujian unit hanya dapat dijalankan secara otomatis apabila kode yang diuji dapat dipanggil tanpa antarmuka. Kelas yang membaca Console.ReadLine di dalam fungsinya atau yang menulis hasilnya langsung ke kontrol form memaksa penguji menyediakan layar, masukan, dan penekanan tombol, sehingga pemeriksaan tersebut tidak dapat diulang secara otomatis tanpa campur tangan manusia. Karena itu logika perhitungan ditempatkan pada kelas biasa yang menerima masukan lewat parameter dan mengembalikan hasil lewat Return, sedangkan antarmuka hanya memanggil kelas tersebut.
Kelas KalkulatorNilai pada topik ini mengikuti pola itu. Tidak ada satu pun pemanggilan Console atau kontrol Windows Forms di dalamnya, sehingga fungsi-fungsinya dapat dipanggil dari uji dengan data tertentu lalu dibandingkan hasilnya dengan nilai yang diharapkan. Pemisahan semacam ini bukan sekadar kerapian, melainkan prasyarat bagi pengujian otomatis, dan prinsip yang sama berlaku pada aplikasi desktop: perhitungan diletakkan pada kelas terpisah dari form agar kelas itu dapat diuji tanpa menjalankan jendela apa pun.
Siklus merah dan hijau#
Kebiasaan menguji lebih dulu membalik urutan yang lazim, yaitu uji ditulis sebelum implementasinya. Tahapannya tetap: tulis satu uji yang menyatakan perilaku yang diharapkan, jalankan lalu lihat kegagalannya, tulis implementasi paling sederhana hingga uji lulus, dan rapikan kode setelahnya. Keadaan gagal disebut tahap merah, sedangkan keadaan lulus disebut tahap hijau, dan kedua istilah itu berasal dari warna keluaran alat uji.
Urutan tersebut membuat uji menyatakan spesifikasi, bukan menyalin perilaku kode yang sudah jadi. Implementasi berhenti begitu seluruh uji hijau, sehingga kode yang tidak diperlukan tidak ditulis. Proses perapian kode (refactor) juga menjadi lebih aman, karena setiap perubahan besar segera diikuti pemanggilan ulang uji sebagai pemeriksa. Kebiasaan ini paling bermanfaat untuk logika perhitungan yang perilakunya dapat dinyatakan sebagai contoh konkret, sedangkan tidak semua kode perlu diuji lebih dulu sepanjang waktu.
Kerangka pengujian yang digunakan adalah xUnit, yaitu kerangka uji untuk .NET yang dipasang melalui paket NuGet. Proyek uji adalah proyek VB.NET terpisah yang merujuk paket tersebut, dan seluruh ujinya dijalankan dengan perintah dotnet test.
Struktur proyek uji xUnit#
Proyek uji dipisahkan dari proyek aplikasi agar kode uji tidak ikut terdistribusi bersama program utama. Berkas proyeknya menyerupai proyek console biasa, tetapi menambahkan tiga paket pengujian.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.1" />
</ItemGroup>
</Project>Tiga paket tersebut masing-masing memiliki peran berbeda. xunit memuat kerangka uji beserta kelas Assert, xunit.runner.visualstudio menjembatani uji xUnit dengan mesin dotnet test, sedangkan Microsoft.NET.Test.Sdk menyediakan titik masuk pengujian untuk perintah tersebut. Properti IsPackable yang bernilai false menandai proyek ini sebagai proyek uji yang tidak pernah dipaketkan menjadi artefak NuGet. Folder proyek bernama UjiNilai memuat tiga berkas, yaitu berkas proyek di atas, kelas yang diuji, dan kelas ujinya.
Kelas uji dengan Fact dan Theory#
Kelas uji adalah kelas biasa yang anggotanya diberi atribut pengujian. Atribut <Fact> menandai satu Sub tanpa parameter sebagai satu kasus uji, sedangkan <Theory> bersama beberapa atribut <InlineData> menjalankan satu Sub yang sama dengan data yang berbeda pada setiap barisnya. Kelas uji memerlukan Imports Xunit di kepalanya.
Imports Xunit
Public Class UjiKalkulatorNilai
<Fact>
Public Sub RataRata_DariTigaNilai_MenghasilkanNilaiTengah()
Dim kalkulator As New KalkulatorNilai()
Dim hasil As Double = kalkulator.RataRata(70, 80, 90)
Assert.Equal(80.0, hasil)
End Sub
<Fact>
Public Sub RataRata_TanpaNilai_MelemparArgumentException()
Dim kalkulator As New KalkulatorNilai()
Assert.Throws(Of ArgumentException)(Sub() kalkulator.RataRata())
End Sub
<Theory>
<InlineData(85, "A")>
<InlineData(75, "B")>
<InlineData(60, "C")>
<InlineData(40, "D")>
Public Sub KonversiHuruf_MengembalikanHurufSesuaiRentang(nilai As Integer, hurufDiharapkan As String)
Dim kalkulator As New KalkulatorNilai()
Assert.Equal(hurufDiharapkan, kalkulator.KonversiHuruf(nilai))
End Sub
End ClassMetode Assert.Equal membandingkan nilai yang diharapkan dengan hasil pemanggilan, sedangkan Assert.Throws memastikan pemanggilan tertentu melempar jenis exception yang diminta. Nama uji mengikuti pola NamaMetode_Kondisi_HasilDiharapkan, misalnya RataRata_DariTigaNilai_MenghasilkanNilaiTengah, sehingga keluaran yang gagal langsung menyebut perilaku mana yang menyimpang. Pada metode bertanda <Theory>, keempat baris InlineData diperlakukan sebagai empat kasus terpisah dengan pasangan nilai 85 dan “A”, 75 dan “B”, 60 dan “C”, serta 40 dan “D”.
Perintah uji dijalankan dari dalam folder proyek.
# tidak-dijalankan
# masuk ke folder proyek uji
cd UjiNilai
# mengompilasi proyek dan menjalankan seluruh uji
dotnet testPada iterasi pertama, uji ditulis lebih dahulu sedangkan implementasinya belum ada. Agar proyek tetap dapat dikompilasi, kelas KalkulatorNilai ditulis sebagai stub, yaitu bentuk kosong yang sengaja mengembalikan nilai sementara, sedangkan kelas ujinya baru memuat satu kasus uji untuk rata-rata.
' tidak-dijalankan
Public Class KalkulatorNilai
Public Function RataRata(ParamArray nilai() As Integer) As Double
Return 0
End Function
End Class Determining projects to restore...
Restored /home/kal/my-project/build-scratch/vb-profesional__pengujian-unit/UjiNilai/UjiNilai.vbproj (in 246 ms).
UjiNilai -> /home/kal/my-project/build-scratch/vb-profesional__pengujian-unit/UjiNilai/bin/Debug/net10.0/UjiNilai.dll
Test run for /home/kal/my-project/build-scratch/vb-profesional__pengujian-unit/UjiNilai/bin/Debug/net10.0/UjiNilai.dll (.NETCoreApp,Version=v10.0)
A total of 1 test files matched the specified pattern.
[xUnit.net 00:00:00.16] UjiNilai.UjiKalkulatorNilai.RataRata_DariTigaNilai_MenghasilkanNilaiTengah [FAIL]
Failed UjiNilai.UjiKalkulatorNilai.RataRata_DariTigaNilai_MenghasilkanNilaiTengah [6 ms]
Error Message:
Assert.Equal() Failure: Values differ
Expected: 80
Actual: 0
Stack Trace:
at UjiNilai.UjiKalkulatorNilai.RataRata_DariTigaNilai_MenghasilkanNilaiTengah() in /home/kal/my-project/build-scratch/vb-profesional__pengujian-unit/UjiNilai/UjiKalkulatorNilai.vb:line 11
at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)
Failed! - Failed: 1, Passed: 0, Skipped: 0, Total: 1, Duration: 15 ms - UjiNilai.dll (net10.0)Keluaran di atas adalah kondisi merah yang diinginkan. Bagian Error Message menunjukkan Assert.Equal menerima nilai 80 yang diharapkan tetapi memperoleh 0 dari stub, dan Stack Trace menunjuk baris uji pemicunya. Kegagalan ini bukan ketidakberesan, melainkan bukti bahwa uji benar-benar memeriksa sesuatu sebelum implementasinya ditulis.
Implementasi minimal sampai hijau#
Implementasi ditulis seminimal mungkin untuk memenuhi perilaku yang disyaratkan uji. Fungsi RataRata menghitung rata-rata lewat Average dan melempar ArgumentException apabila dipanggil tanpa argumen, sedangkan KonversiHuruf memetakan rentang nilai dengan Select Case.
Public Class KalkulatorNilai
' rata-rata dari sejumlah nilai; tanpa argumen dianggap tidak sah
Public Function RataRata(ParamArray nilai() As Integer) As Double
If nilai.Length = 0 Then
Throw New ArgumentException("Paling sedikit satu nilai diperlukan.")
End If
Return nilai.Average()
End Function
' konversi nilai angka menjadi huruf predikat
Public Function KonversiHuruf(nilai As Integer) As String
Select Case nilai
Case Is >= 80
Return "A"
Case Is >= 70
Return "B"
Case Is >= 50
Return "C"
Case Else
Return "D"
End Select
End Function
End Class Determining projects to restore...
All projects are up-to-date for restore.
UjiNilai -> /home/kal/my-project/build-scratch/vb-profesional__pengujian-unit/UjiNilai/bin/Debug/net10.0/UjiNilai.dll
Test run for /home/kal/my-project/build-scratch/vb-profesional__pengujian-unit/UjiNilai/bin/Debug/net10.0/UjiNilai.dll (.NETCoreApp,Version=v10.0)
A total of 1 test files matched the specified pattern.
Passed! - Failed: 0, Passed: 6, Skipped: 0, Total: 6, Duration: 19 ms - UjiNilai.dll (net10.0)Ringkasan akhir melaporkan enam kasus lulus, yaitu dua metode <Fact> ditambah empat baris data pada metode <Theory>. Uji RataRata_TanpaNilai_MelemparArgumentException ikut tercakup sejak implementasi ini, sehingga perilaku tanpa argumen sudah terjaga tanpa uji tambahan. Setelah seluruhnya hijau, kode boleh dirapikan, misalnya memindahkan ambang huruf menjadi konstanta, dan uji dijalankan ulang setelah setiap perapian untuk memastikan perilakunya tidak bergeser.
Kesalahan umum#
Kesalahan pertama adalah menulis implementasi lebih dahulu lalu menyusun uji yang menyalin perilakunya. Uji semacam itu hanya mengulang kode yang sudah ada sehingga tidak pernah menemukan kesalahan konsep. Perbaikannya adalah menuliskan uji dari kebutuhan yang diketahui, misalnya rata-rata 70, 80, dan 90 harus 80, sebelum memikirkan caranya.
Kesalahan kedua adalah memberi nama uji yang generik seperti Uji1 atau UjiRataRata. Saat keluaran melaporkan kegagalan, nama itulah satu-satunya petunjuk, sehingga nama tanpa kondisi dan hasil yang diharapkan memaksa pembacaan ulang kode. Nama uji di atas sengaja panjang karena berfungsi sebagai ringkasan perilakunya.
Kesalahan ketiga adalah menyisipkan Console.WriteLine atau pembacaan masukan di dalam kelas yang diuji. Fungsi semacam itu tidak dapat diperiksa secara otomatis karena hasilnya tercampur dengan keluaran layar. Pindahkan keluaran ke lapisan pemanggil, dan biarkan fungsi mengembalikan nilai lewat Return sebagaimana KalkulatorNilai pada topik ini.
Ringkasan#
- Pengujian unit memeriksa kelas atau fungsi secara otomatis tanpa antarmuka, sehingga logika perhitungan harus dipisahkan dari kode
Consolemaupun form. - Kebiasaan menguji lebih dulu bergerak dari uji yang merah, implementasi minimal sampai hijau, lalu perapian kode dengan uji tetap hijau sebagai pemeriksa.
- Atribut
<Fact>menandai satu kasus uji,<Theory>dengan<InlineData>menjalankan banyak kasus data dari satu metode, sedangkanAssert.EqualdanAssert.Throwsmemeriksa hasil serta exception yang dilempar.
Referensi#
- Unit testing Visual Basic with dotnet test and xUnit — Microsoft Learn — Langkah membuat dan menjalankan proyek uji xUnit berbahasa Visual Basic.
- Unit testing best practices — Microsoft Learn — Prinsip penamaan uji dan pemisahan logika agar uji tetap andal.