Dasar xUnit

Fact, Theory, dan assertion.

Daftar isi

xUnit adalah framework pengujian untuk .NET, yakni pustaka yang menyediakan kerangka untuk menuliskan dan menjalankan pengujian otomatis. Topik ini membahas pembuatan proyek uji dengan templat bawaan SDK, penandaan metode pengujian menggunakan atribut Fact dan Theory, serta pemeriksaan perilaku kode dengan assertion, yaitu pernyataan yang menggagalkan pengujian bila hasilnya tidak sesuai harapan. Setelah topik ini dipelajari, setiap perubahan kode dapat diperiksa ulang dengan satu perintah dotnet test.

Struktur proyek uji#

Proyek uji xUnit dibuat dengan perintah dotnet new xunit. Templat ini menghasilkan berkas proyek UjiDasar.csproj, satu kelas contoh bernama UnitTest1.cs, serta empat rujukan package yang diperlukan, yaitu xunit sebagai pustaka atribut dan assertion, xunit.runner.visualstudio sebagai penghubung menuju mesin uji, Microsoft.NET.Test.Sdk sebagai penyelenggara sesi pengujian, dan coverlet.collector untuk pengukuran cakupan kode yang dibahas pada topik lain. Templat juga mendaftarkan Xunit sebagai global using, sehingga berkas pengujian tidak perlu menulis using Xunit; secara berulang. Perintah pembuatannya dijalankan pada folder kerja kosong.

bash
# membuat proyek uji xUnit di dalam folder kerja
dotnet new xunit -o UjiDasar 2>&1 | grep "Restore succeeded"
text Keluaran
Restore succeeded.

Kelas yang diuji ditempatkan di dalam proyek uji yang sama. Pada proyek sungguhan, kode aplikasi biasanya berada di proyek terpisah yang dirujuk oleh proyek uji, sedangkan contoh di topik ini menggabungkannya agar langkahnya pendek. Kelas yang diuji adalah Kalkulator, yaitu kelas statis berisi operasi statistik sederhana atas nilai ujian.

csharp Kalkulator.cs
// Kelas yang diuji: operasi statistik sederhana atas nilai ujian.
public static class Kalkulator
{
    public static int Tambah(int a, int b) => a + b;

    public static double RataRata(int[] nilai)
    {
        if (nilai is null || nilai.Length == 0)
        {
            throw new ArgumentException("Array nilai tidak boleh kosong.");
        }

        int total = 0;
        foreach (int n in nilai)
        {
            total += n;
        }
        return (double)total / nilai.Length;
    }
}

Fact: satu metode untuk satu kasus#

Atribut Fact menandai sebuah metode tanpa parameter sebagai satu kasus pengujian yang utuh. Konvensi penamaannya mengikuti pola Metode_Skenario_Harapan, misalnya Tambah_MengembalikanJumlahDuaNilai, sehingga nama pengujian yang gagal langsung menjelaskan skenarionya tanpa membuka berkasnya. Di dalam metode tersebut, kode menyiapkan masukan, memanggil metode yang diuji, lalu memeriksa hasilnya dengan kelas Assert.

Berkas pengujian berikut memuat tiga metode Fact dan dua metode Theory sekaligus, sehingga seluruh konsep topik ini berada dalam satu berkas yang dapat disalin utuh. Metode RataRata_ArrayNull_MelemparArgumentException melewatkan null dengan tanda ! setelahnya, yang menyatakan peringatan nullable pada baris itu sengaja diabaikan karena pengujian memang menguji masukan null.

csharp KalkulatorTests.cs
using Xunit;

public class KalkulatorTests
{
    [Fact]
    public void Tambah_MengembalikanJumlahDuaNilai()
    {
        Assert.Equal(5, Kalkulator.Tambah(2, 3));
    }

    [Fact]
    public void RataRata_ArrayKosong_MelemparArgumentException()
    {
        Assert.Throws<ArgumentException>(() => Kalkulator.RataRata(new int[0]));
    }

    [Fact]
    public void RataRata_ArrayNull_MelemparArgumentException()
    {
        Assert.Throws<ArgumentException>(() => Kalkulator.RataRata(null!));
    }

    [Theory]
    [InlineData(0, 0, 0)]
    [InlineData(10, 20, 30)]
    [InlineData(-5, 5, 0)]
    public void Tambah_BanyakPasanganNilai(int a, int b, int diharapkan)
    {
        Assert.Equal(diharapkan, Kalkulator.Tambah(a, b));
    }

    [Theory]
    [InlineData(new int[] { 80, 90, 100 }, 90.0)]
    [InlineData(new int[] { 70 }, 70.0)]
    public void RataRata_MenghitungRerataNilai(int[] nilai, double diharapkan)
    {
        Assert.Equal(diharapkan, Kalkulator.RataRata(nilai));
    }
}

Setelah kedua berkas disalin ke proyek uji dan contoh bawaan templat dihapus, seluruh pengujian dijalankan dengan dotnet test. Baris ringkasan yang dihasilkan memuat empat angka, yaitu jumlah kasus yang gagal, yang lulus, yang dilewati, dan totalnya.

bash
# menyalin kelas yang diuji dan berkas pengujian ke dalam proyek uji
# contoh bawaan templat dihapus karena sudah digantikan
cp Kalkulator.cs KalkulatorTests.cs UjiDasar/
rm UjiDasar/UnitTest1.cs

# menjalankan seluruh pengujian, hanya baris ringkasan yang ditampilkan
dotnet test UjiDasar 2>&1 | grep -E "Passed!|Failed!" | sed 's/, Duration.*//'
text Keluaran
Passed!  - Failed:     0, Passed:     8, Skipped:     0, Total:     8
bash
# menghapus proyek uji sementara setelah latihan selesai
rm -rf UjiDasar

Theory: satu metode untuk banyak data#

Atribut Theory menandai metode berparameter sebagai pengujian yang dijalankan sekali untuk setiap baris data yang disediakan [InlineData]. Metode Tambah_BanyakPasanganNilai memuat tiga baris data, sehingga mesin uji menjalankannya tiga kali sebagai tiga kasus terpisah, dan kegagalan satu baris tidak menghentikan baris lainnya. Parameter metode diisi secara berurutan dari setiap baris [InlineData], sedangkan parameter diharapkan memuat nilai yang dibandingkan oleh assertion.

Pemisahan data dari logika pengujian membuat penambahan kasus menjadi murah. Bila perilaku Tambah perlu diuji dengan pasangan baru, satu baris [InlineData] cukup ditambahkan tanpa menyentuh isi metode. Untuk data yang lebih besar atau dibangun saat runtime, xUnit menyediakan [MemberData] dan [ClassData] sebagai sumber data alternatif, dengan cara pemakaiannya yang sama pada atribut Theory.

Hitungan pada baris ringkasan sebelumnya dapat diurai dari isi berkas, yaitu tiga kasus dari metode Fact dan lima kasus dari seluruh baris [InlineData], sehingga totalnya delapan. Persamaan ini berguna sebagai pemeriksaan cepat apakah seluruh baris data benar-benar terbaca oleh mesin uji.

Assertion yang tersedia#

Kelas Assert menyediakan metode statis untuk setiap bentuk pemeriksaan yang lazim. Setiap metode menggagalkan pengujian dengan pesan yang informatif apabila pemeriksaannya tidak terpenuhi, dan pengujian berhenti pada assertion yang pertama kali gagal. Metode yang paling sering digunakan dirangkum pada tabel berikut.

MetodePemeriksaan
Assert.Equal(diharapkan, aktual)dua nilai sama, termasuk bentuk dengan toleransi desimal
Assert.NotEqual(a, b)dua nilai tidak sama
Assert.True(kondisi)kondisi bernilai true
Assert.Null(x)referensi bernilai null
Assert.NotNull(x)referensi tidak bernilai null
Assert.Throws<T>(() => ...)kode melempar exception bertipe T
Assert.Contains(sub, teks)teks memuat bagian yang dicari
Assert.Single(koleksi)koleksi berisi tepat satu elemen

Urutan argumen Assert.Equal perlu diperhatikan karena nilainya yang diharapkan ditulis lebih dahulu, sedangkan nilai aktual ditulis belakangan. Apabila urutannya terbalik, pengujian tetap berjalan tetapi pesan kegagalannya menyebut “expected” dan “actual” secara tertukar, sehingga pembacaannya menyesatkan. Untuk bilangan desimal, bentuk Assert.Equal(diharapkan, aktual, 2) menetapkan toleransi dua angka di belakang koma sehingga hasil pembagian yang membulat tidak menggagalkan pengujian.

Kesalahan umum#

Kelas uji atau metode uji yang tidak public membuat pengujian tidak ditemukan. xUnit hanya menemukan metode pengujian yang dapat diakses dari luar, sehingga temuan berupa baris ringkasan dengan jumlah total nol biasanya berarti pengubah aksesnya keliru. Perbaikannya adalah menambahkan public pada kelas uji dan pada setiap metode yang bertanda Fact atau Theory.

Kekeliruan berikutnya adalah menaruh logika bersyarat di dalam metode pengujian. Pengujian yang berisi if membuat hasilnya bergantung pada jalur yang dijalankan, sehingga baris data yang gagal bisa tidak pernah diperiksa assertionnya. Setiap skenario sebaiknya menjadi metode Fact tersendiri atau baris [InlineData] tersendiri agar kegagalan selalu bermuara pada satu kasus yang jelas.

Ringkasan#

  • dotnet new xunit membuat proyek uji berisi rujukan package xUnit beserta mesin uji yang dijalankan dengan dotnet test.
  • Atribut Fact menandai metode tanpa parameter sebagai satu kasus pengujian, sedangkan Theory dengan [InlineData] menghasilkan satu kasus untuk setiap baris data.
  • Kelas Assert menggagalkan pengujian bila pemeriksaannya tidak terpenuhi, dan Assert.Throws memeriksa pelemparan exception.
  • Baris ringkasan dotnet test memuat jumlah kasus gagal, lulus, dilewati, dan total, yang seharusnya sama dengan jumlah metode Fact ditambah jumlah baris [InlineData].

Referensi#