Build, run, dan test

Menyusun tata letak proyek dan menjalankan subperintah mojo dari run sampai pengujian TestSuite.

Daftar isi

Kerja produksi dengan Mojo berpusat pada beberapa subperintah, yaitu mojo run untuk mencoba berkas, mojo build untuk menghasilkan executable, mojo format untuk merapikan tata letak, mojo doc untuk mengubah deklarasi menjadi data terstruktur, serta pengujian TestSuite yang dijalankan sebagai program biasa. Topik ini menyusun empat berkas contoh menjadi satu tata letak proyek kecil, lalu menjalankan setiap subperintah di atasnya, lengkap dengan keluaran aslinya. Hasil akhirnya adalah siklus kerja yang utuh, mulai dari mengubah kode, mengujinya, memformatnya, sampai menghasilkan dokumentasi terstruktur.

Tata letak proyek dan mojo run#

Proyek pada topik ini menata dua peran berkas, yaitu modul yang menyimpan logika dan berkas penggerak yang mengimpornya. Seluruhnya berada pada satu direktori tanpa subfolder, sehingga pencarian modul berlangsung tanpa konfigurasi apa pun.

text Struktur direktori proyek
├── statistik.mojo
├── utama.mojo
├── uji-statistik.mojo
└── jelek.mojo

Modul statistik.mojo memuat dua fungsi ringkasan beserta satu varian yang memunculkan error ketika daftar kosong.

mojo statistik.mojo
def rata_rata(nilai: List[Float64]) -> Float64:
    var total = 0.0
    for x in nilai:
        total += x
    return total / Float64(len(nilai))

def maksimum(nilai: List[Float64]) -> Float64:
    var terbesar = nilai[0]
    for x in nilai:
        if x > terbesar:
            terbesar = x
    return terbesar

def rata_rata_aman(nilai: List[Float64]) raises -> Float64:
    if len(nilai) == 0:
        raise Error("daftar kosong")
    return rata_rata(nilai)

Berkas penggerak mengimpor dua fungsi pertama dan mencetak ringkasannya.

mojo utama.mojo
from statistik import rata_rata, maksimum

def main() raises:
    var nilai: List[Float64] = [7.5, 8.0, 6.5, 9.0]
    print("rata-rata:", rata_rata(nilai))
    print("maksimum:", maksimum(nilai))
bash
# jalankan program penggerak bersama modulnya
mojo run utama.mojo
text Keluaran
rata-rata: 7.75
maksimum: 9.0

Subperintah run mengompilasi lalu langsung mengeksekusi berkas dalam satu langkah, sehingga keluaran setiap perubahan kode segera terlihat dan tidak ada hasil kompilasi yang perlu dipelihara. Berikut daftar subperintah mojo menurut bantuan resminya.

SubperintahTugas menurut bantuan resmi
runMengompilasi dan mengeksekusi berkas Mojo.
buildMenghasilkan executable dari berkas Mojo.
replMembuka sesi REPL Mojo.
debugMemulai debugger Mojo lewat command line atau editor eksternal.
precompileMembuat paket precompiled.
formatMemformat berkas sumber Mojo.
docMengompilasi docstring dari berkas Mojo.
demangleMengembalikan nama simbol yang dimangle.

Kompilasi menjadi executable dengan mojo build#

Subperintah build mengompilasi satu berkas menjadi program executable yang berdiri sendiri, sehingga program dapat dijalankan lagi tanpa proses kompilasi ulang. Opsi -o menetapkan nama program hasilnya.

bash
# kompilasi utama.mojo menjadi program bernama program
mojo build utama.mojo -o program
bash
# program hasil build dijalankan langsung tanpa mojo run
./program
text Keluaran
rata-rata: 7.75
maksimum: 9.0

Keluaran ./program identik dengan keluaran mojo run utama.mojo karena keduanya memproses berkas sumber yang sama, hanya saja versi build sudah berupa biner jadi. Perbedaan praktisnya terletak pada siklus kerja, yaitu run untuk percobaan berulang dan build untuk menghasilkan program yang dikirim ke tempat lain.

Pengujian fungsi dengan TestSuite#

Pengujian pada Mojo ditulis sebagai fungsi biasa berawalan test_, lalu seluruhnya dieksekusi oleh TestSuite dari modul std.testing. Pengujian dijalankan seperti program biasa dengan mojo run.

Berkas uji berikut memuat empat fungsi test_. Metode assert_almost_equal membandingkan bilangan pecahan dengan toleransi atol, assert_equal menuntut kesamaan persis, dan assert_raises dipakai sebagai context manager untuk memastikan rata_rata_aman memunculkan error pada daftar kosong.

mojo uji-statistik.mojo
from std.testing import assert_equal, assert_almost_equal, assert_raises, TestSuite
from statistik import rata_rata, maksimum, rata_rata_aman

def test_rata_rata() raises:
    assert_almost_equal(rata_rata([7.5, 8.0, 6.5, 9.0]), 7.75, atol=0.000001)

def test_maksimum() raises:
    assert_equal(maksimum([7.5, 8.0, 6.5, 9.0]), 9.0)

def test_maksimum_satu_nilai() raises:
    assert_equal(maksimum([42.0]), 42.0)

def test_rata_rata_aman_kosong() raises:
    with assert_raises():
        _ = rata_rata_aman([])

def main() raises:
    TestSuite.discover_tests[__functions_in_module()]().run()

Baris TestSuite.discover_tests[__functions_in_module()]().run() menjadi gerbang eksekusinya. Fungsi discover_tests mengumpulkan fungsi pada modul ini melalui __functions_in_module(), membentuk rangkaian pengujian darinya, lalu menjalankannya satu per satu.

bash
# tidak-dijalankan
# durasi tiap pengujian bervariasi antar mesin
mojo run uji-statistik.mojo
text Keluaran yang diharapkan
Running 4 tests for /home/kal/my-project/content/mojo/package-dan-perkakas/build-run-test/uji-statistik.mojo
    PASS [ 0.001 ] test_rata_rata
    PASS [ 0.001 ] test_maksimum
    PASS [ 0.001 ] test_maksimum_satu_nilai
    PASS [ 0.002 ] test_rata_rata_aman_kosong
--------
Summary [ 0.002 ] 4 tests run: 4 passed , 0 failed , 0 skipped

Nilai di dalam kurung siku adalah durasi tiap pengujian yang bervariasi antarmesin, sedangkan baris Summary menampilkan rekap akhirnya. Cakupan pengujian diatur melalui opsi --only yang menjalankan fungsi yang disebut dan melewatkan sisanya, atau --skip yang bekerja sebaliknya.

bash
# tidak-dijalankan
# durasi tiap pengujian bervariasi antar mesin
mojo run uji-statistik.mojo --only test_maksimum
text Keluaran yang diharapkan
Running 4 tests for /home/kal/my-project/content/mojo/package-dan-perkakas/build-run-test/uji-statistik.mojo
    SKIP [ 0.001 ] test_rata_rata
    PASS [ 0.001 ] test_maksimum
    SKIP [ 0.001 ] test_maksimum_satu_nilai
    SKIP [ 0.001 ] test_rata_rata_aman_kosong
--------
Summary [ 0.001 ] 4 tests run: 1 passed , 0 failed , 3 skipped

Empat kasus tetap dicatat pada kedua keluaran agar rekapnya konsisten, hanya statusnya yang berubah menjadi SKIP. Rekap 1 passed , 3 skipped menunjukkan bahwa fungsi test_maksimum dijalankan dan tiga sisanya dilewati sesuai opsi --only.

Baris _ = rata_rata_aman([]) pada kasus terakhir menyandingkan pola yang lazim pada pengujian, yaitu memanggil fungsi yang hasilnya tidak dipakai. Tanpa penugasan ke _, compiler menyertakan peringatan bahwa nilai kembalian tidak digunakan, sedangkan pola discard seperti ini menandakan bahwa hasilnya sengaja diabaikan. Pola yang sama dipakai pada pengujian yang memanggil fungsi hanya untuk memastikan errornya muncul melalui assert_raises.

Pemformatan dan dokumentasi terstruktur#

Subperintah format merapikan spasi dan tata letak menurut gaya bawaan Mojo, sehingga perdebatan penataan kode tidak lagi menjadi urusan manusia. Agar berkas contoh pada topik ini tetap dalam bentuk awalnya, pemformatan diperagakan pada salinan di direktori sementara.

mojo jelek.mojo
def luas(a: Int,b: Int)->Int:
    return a*b


def main():
    var x = luas( 3, 4 )
    print( x )
bash
# salin berkas ke direktori sementara sebelum diformat
mkdir -p /tmp/rapikan
cp jelek.mojo /tmp/rapikan/jelek.mojo
# rapikan tata letak salinan tersebut
mojo format /tmp/rapikan/jelek.mojo
text Keluaran
reformatted /tmp/rapikan/jelek.mojo

All done! ✨ 🍰 ✨
1 file reformatted.
bash
# tampilkan isi salinan setelah format
cat /tmp/rapikan/jelek.mojo
text Keluaran
def luas(a: Int, b: Int) -> Int:
    return a * b


def main():
    var x = luas(3, 4)
    print(x)

Perbandingan sebelum dan sesudah menunjukkan tiga perubahan, yaitu spasi ditambahkan di sekitar operator *, spasi berlebih di dalam tanda kurung dihapus, dan dua baris kosong antarfungsi dipertahankan. Format ini dijalankan ulang setiap kali kode berubah, atau dilampirkan pada langkah penyuntingan agar berkas selalu kembali rapi.

Subperintah terakhir, mojo doc, mengumpulkan deklarasi sebuah berkas beserta docstring, yakni teks dokumentasi yang ditulis di dalam kode, menjadi satu dokumen JSON. Memfilter baris tanda tangan fungsinya memberikan ringkasan API yang ringkas.

bash
# tampilkan baris tanda tangan fungsi dari data dokumentasi
mojo doc statistik.mojo | grep '"signature"'
text Keluaran
            "signature": "def rata_rata(nilai: List[Float64]) -> Float64",
            "signature": "def maksimum(nilai: List[Float64]) -> Float64",
            "signature": "def rata_rata_aman(nilai: List[Float64]) -> Float64",

Baris-baris di atas tetap bagian dari satu dokumen JSON yang memuat argumen, tipe kembalian, dan keterangan tiap fungsi. Data semacam ini dibaca alat lain, misalnya generator dokumentasi situs atau pemeriksa API, tanpa perlu mengurai ulang kode sumbernya.

Ringkasan#

  • mojo run mengompilasi dan mengeksekusi berkas dalam satu langkah, sedangkan mojo build menghasilkan executable dengan nama dari opsi -o yang dijalankan sendiri.
  • Pengujian ditulis sebagai fungsi test_ dan dijalankan melalui TestSuite.discover_tests[__functions_in_module()]().run() dengan mojo run, lengkap dengan opsi --only dan --skip.
  • mojo format merapikan tata letak berkas, dan mojo doc menghasilkan data dokumentasi JSON dari deklarasi berkas.

Referensi#