Panduan Pasukan Lucee MY

TestBox lanjutan: mock, suite dan integrasi CI

TestBox lanjutan untuk projek CFML: memalsukan kaedah dan kebergantungan dengan MockBox, menyusun suite, dan menjalankan ujian dalam CI secara automatik.

TestBox lanjutan: mock, suite dan integrasi CI
0
Kandungan

TestBox lanjutan: mock, suite dan integrasi CI

Ujian unit yang jujur tidak boleh bergantung pada pangkalan data hidup, API luar, atau komponen lain yang lambat dan tidak menentu. Apabila kod anda memanggil sesuatu di luar dirinya, anda perlu memalsukan kebergantungan itu supaya ujian menguji satu benda pada satu masa, dan hasilnya sama setiap kali ia dijalankan. Artikel ini menunjukkan cara memalsukan kaedah dan kebergantungan dengan MockBox, cara menyusun banyak spec menjadi satu suite yang boleh dijalankan serentak, dan cara memasukkan suite itu ke dalam saluran CI supaya ia berjalan sendiri pada setiap tolakan kod. Setiap contoh diuji pada TestBox 7.2.2 yang berjalan di atas Lucee 7.1. Jika anda belum pernah menulis spec, baca panduan TestBox asas dahulu, kerana artikel ini menganggap anda sudah tahu describe, it dan expect.

Kenapa mock, bukan panggil terus

Ujian unit yang memanggil pangkalan data sebenar mempunyai tiga masalah besar. Pertama, ia perlahan, dan ujian yang perlahan membuatkan orang berhenti menjalankannya. Kedua, ia tidak menentu: satu ujian yang lulus hari ini boleh gagal esok kerana data dalam jadual berubah, bukan kerana kod anda rosak. Ketiga, ia menguji terlalu banyak benda serentak, jadi apabila ujian gagal anda tidak tahu bahagian mana yang sebenarnya rosak.

Mock menyelesaikan ketiga-tiganya. Sebuah mock ialah objek tiruan yang menggantikan kebergantungan sebenar dan memulangkan nilai yang anda tentukan sendiri. Ujian kemudian hanya memeriksa logik kod anda, bukan kebergantungan itu. Nilai itu ditetapkan dalam ujian, jadi hasilnya pasti, cepat, dan terasing. Inilah bezanya antara ujian yang anda jalankan setiap hari dengan ujian yang anda tinggalkan selepas minggu pertama.

Memalsukan kaedah dengan MockBox

MockBox ialah enjin pemalsuan yang disertakan bersama TestBox. Di dalam spec, anda tidak perlu mencipta MockBox secara manual, kerana BaseSpec sudah menyediakan dua pembantu: prepareMock() untuk membalut objek yang sudah wujud, dan createMock() untuk mencipta mock daripada nama kelas. Contoh berikut memalsukan kaedah tambah pada objek Kalkulator:

component extends="testbox.system.BaseSpec" {
    function run() {
        describe("MockBox", function() {
            it("boleh memalsukan kaedah", function() {
                calc = prepareMock(new uji.model.Kalkulator());
                calc.$("tambah").$args(2,3).$results(99);
                expect(calc.tambah(2,3)).toBe(99);
            });
        });
    }
}

Baris prepareMock(new uji.model.Kalkulator()) membalut objek sebenar dan menambah keupayaan pemalsuan padanya tanpa membuang kaedah asal. Rantaian $("tambah") memilih kaedah yang hendak dipalsukan, $args(2,3) menyatakan ia hanya memalsukan panggilan dengan hujah tepat itu, dan $results(99) menetapkan nilai pulangan. Selepas itu, calc.tambah(2,3) memulangkan 99 dan bukannya 5, dan expect(...).toBe(99) membuktikan pemalsuan itu berjaya. Perhatikan bahawa prepareMock dipanggil di dalam it, bukan di peringkat templat, dan tidak ada var atau local. di sini, kerana itu akan merosakkan konteks spec.

Memalsukan kebergantungan dengan $property

Dalam aplikasi sebenar, komponen jarang berdiri sendiri. Ambil contoh KiraInvois yang bergantung pada KadarCukai untuk mendapatkan kadar cukai semasa, disuntik melalui inject seperti yang diterangkan dalam panduan WireBox. Dalam ujian unit, anda tidak mahu kadar cukai sebenar, anda mahu nilai yang anda kawal, supaya pengiraan boleh disemak dengan tepat:

component extends="testbox.system.BaseSpec" {
    function run() {
        describe("KiraInvois", function() {
            it("mengira cukai guna kadar yang dipalsukan", function() {
                invois = prepareMock(new uji.model.KiraInvois());
                kadarPalsu = prepareMock(new uji.model.KadarCukai());
                kadarPalsu.$("kadar").$results(0.10);
                invois.$property("kadarCukai", "variables", kadarPalsu);
                expect(invois.cukai(100)).toBe(10);
            });
        });
    }
}

Di sini kadarPalsu memalsukan kaedah kadar() supaya ia sentiasa memulangkan 0.10. Kaedah $property("kadarCukai", "variables", kadarPalsu) kemudian menyuntik mock itu ke dalam skop variables objek invois, menggantikan kebergantungan sebenar. Apabila cukai(100) berjalan, ia membaca variables.kadarCukai.kadar() dan mendapat 0.10, jadi hasilnya tepat 10. Ini membuktikan logik pengiraan cukai tanpa menyentuh mana-mana sumber kadar sebenar, dan ia menunjukkan teknik teras pemalsuan kebergantungan: jangan panggil yang sebenar, suntik yang palsu.

Menyusun spec menjadi suite

Satu spec berguna untuk satu komponen, tetapi projek sebenar mempunyai puluhan komponen. TestBox membolehkan anda meletakkan semua fail spec dalam satu direktori, contohnya uji.specs, dan ia akan menemui serta menjalankan setiap fail yang berakhir dengan Spec.cfc. Setiap describe menjadi satu suite, dan setiap it menjadi satu spec di dalamnya.

Untuk menjalankan keseluruhan direktori dan membaca ringkasan hasilnya, gunakan runRaw() yang memulangkan objek TestResult, kemudian baca ringkasannya melalui getMemento():

tb = new testbox.system.TestBox();
laporan = tb.runRaw(directory="uji.specs");
m = laporan.getMemento();
writeOutput("suite=" & m.totalSuites & " | spec=" & m.totalSpecs & " | lulus=" & m.totalPass & " | gagal=" & m.totalFail & " | ralat=" & m.totalError);

Pada rig ujian, direktori uji.specs mengandungi tiga fail: KalkulatorSpec daripada panduan TestBox asas dengan tiga spec, MockSpec dan KiraInvoisSpec daripada artikel ini dengan satu spec setiap satu. Blok di atas memulangkan suite=3 | spec=5 | lulus=5 | gagal=0 | ralat=0. Bezakan run() dengan runRaw(): run() memulangkan laporan HTML penuh yang bagus untuk dilihat dalam pelayar, manakala runRaw() memberi anda objek hasil untuk diproses atau dicetak ringkas seperti di atas.

Kaedah mock yang biasa digunakan

Jadual berikut menyenaraikan kaedah MockBox yang paling kerap anda perlukan. Semua kaedah ini tersedia pada objek yang telah dibalut dengan prepareMock():

KaedahKegunaan
prepareMock(objek)Balut objek sedia ada sebagai mock, kekalkan kaedah asal
$("kaedah")Pilih kaedah yang hendak dipalsukan
$args(...)Padankan hanya panggilan dengan hujah tertentu
$results(nilai)Tetapkan nilai pulangan untuk kaedah yang dipalsukan
$property(nama, skop, objek)Suntik kebergantungan palsu ke dalam skop tertentu
$once() / $times(n)Sahkan kaedah dipanggil tepat bilangan yang dikehendaki

Dengan enam kaedah ini anda boleh memalsukan hampir mana-mana kebergantungan dan mengesahkan bahawa kod anda memanggilnya dengan betul, bukan sekadar memeriksa nilai pulangan.

Menjalankan suite dalam CI

Ujian hanya berguna jika ia berjalan secara automatik, bukan apabila anda teringat untuk menjalankannya. Saluran CI menjalankan suite pada setiap tolakan kod dan menolak perubahan yang memecahkan ujian. CommandBox menyediakan arahan testbox run yang menjalankan suite anda dari baris arahan, dan ia sesuai untuk dipanggil daripada mana-mana pelayan CI. Pastikan projek anda disimpan dalam Git seperti yang diterangkan dalam panduan Git untuk projek CFML, kemudian tambahkan langkah ini:

box install testbox --production
box testbox run

Arahan pertama memasang TestBox sebagai kebergantungan projek, dan arahan kedua menjalankan keseluruhan suite dan memulangkan kod keluar bukan sifar apabila ada ujian yang gagal, supaya CI tahu bahawa binaan itu rosak. Konfigurasi penuh untuk pelayan CI seperti GitHub Actions kelihatan begini:

name: Ujian CFML
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Pasang CommandBox
        run: |
          curl -fsSL https://downloads.ortussolutions.com/debs/gpg | sudo gpg --dearmor -o /usr/share/keyrings/ortus.gpg
          echo "deb [signed-by=/usr/share/keyrings/ortus.gpg] https://downloads.ortussolutions.com/debs/noarch /" | sudo tee /etc/apt/sources.list.d/commandbox.list
          sudo apt-get update && sudo apt-get install -y commandbox
      - name: Jalankan suite TestBox
        run: box testbox run

Apabila CI ini berjalan, ia menghidupkan CommandBox, memasang TestBox, dan menjalankan suite pada setiap tolakan serta setiap permintaan tarik. Ujian yang gagal menyekat perubahan daripada digabungkan, jadi kualiti dijaga di pintu masuk dan bukannya dibaiki selepas ia sudah masuk ke pengeluaran. Untuk mengurus pelayan dan pembinaan yang lebih lengkap, panduan CommandBox lanjutan menerangkan cara menghidupkan pelayan tempatan yang stabil untuk ujian integrasi.

Sumber

Soalan Lazim

Adakah saya perlu mencipta MockBox secara manual dalam spec?

Tidak. BaseSpec menyediakan prepareMock() dan createMock() yang memanggil MockBox untuk anda. Guna kedua-dua pembantu ini di dalam it, dan jangan cipta objek MockBox secara terus.

Apakah beza prepareMock dengan createMock?

prepareMock() membalut objek yang sudah wujud dan mengekalkan kaedah asalnya, jadi ia sesuai untuk memalsukan sebahagian kaedah sahaja. createMock() mencipta mock daripada nama kelas dengan semua kaedah dikosongkan, sesuai untuk kebergantungan yang anda mahu kawal sepenuhnya.

Kenapa ujian saya masih memanggil pangkalan data selepas saya mock?

Kemungkinan besar anda memalsukan kaedah yang salah, atau kebergantungan itu tidak disuntik ke tempat yang dibaca oleh kod. Semak nama kaedah pada $(), dan pastikan $property() menyuntik ke skop yang sama yang dibaca oleh komponen, biasanya variables.

Bagaimana saya tahu suite lulus dalam skrip CI?

Guna runRaw() dan getMemento() untuk membaca totalPass dan totalFail, atau jalankan box testbox run yang memulangkan kod keluar bukan sifar apabila ada kegagalan. Kod keluar itu yang dibaca oleh CI untuk memutuskan sama ada binaan lulus atau gagal.

Bolehkah saya menguji kod yang membaling ralat?

Boleh. Gunakan expect(function(){ ... }).toThrow() untuk membuktikan kod membaling ralat, dan $throws() untuk membuat mock membaling ralat apabila dipanggil. Teknik menangkap dan mengendalikan ralat selanjutnya diterangkan dalam panduan pengendalian ralat cftry.

0 komen

0 komen

Log masuk untuk menjawab atau memberi komen.

Log masuk