Mengembangkan mesin status dengan pengembangan yang digerakkan oleh pengujian
Karena model state machine banyak digunakan dalam sistem tertanam, artikel ini membahas beberapa strategi untuk mengembangkan perangkat lunak state machine (SM) di bawah pendekatan Test-Driven Development (TDD). Publikasi ini dimulai dengan menjelaskan konsep dasar state machine dan teknik TDD. Akhirnya, ia memperkenalkan metode sederhana dan teratur untuk mengembangkan perangkat lunak mesin negara yang ditulis dalam C menggunakan pendekatan TDD.
Model SM terdiri dari status, transisi, dan tindakan. Sementara keadaan adalah kondisi sistem atau elemen, transisi adalah jalur dari satu keadaan ke keadaan lain, biasanya diprakarsai oleh peristiwa yang menarik yang menghubungkan keadaan pendahulu (sumber) dengan keadaan (target) berikutnya. Perilaku aktual yang dijalankan oleh elemen direpresentasikan dalam tindakan.
Dalam mesin keadaan UML, tindakan dapat dikaitkan dengan masuk ke keadaan, keluar dari keadaan, transisi per se, atau apa yang disebut 'transisi internal' atau 'reaksi'. Semua formalisme mesin negara (termasuk mesin negara UML) secara universal mengasumsikan bahwa mesin negara menyelesaikan pemrosesan setiap peristiwa sebelum dapat mulai memproses yang berikutnya. Model eksekusi ini disebut Run To Completion (RTC). Dalam model ini, tindakan mungkin memerlukan waktu, tetapi setiap peristiwa yang tertunda harus menunggu hingga mesin status selesai — termasuk seluruh tindakan keluar, tindakan transisi, dan urutan tindakan entri dalam urutan itu.
Sebelum membahas strategi untuk mengembangkan mesin negara dengan menggunakan TDD, perlu disebutkan definisi, kepentingan, dan aplikasinya.
Pertama-tama, TDD adalah teknik untuk membangun perangkat lunak secara bertahap. Sederhananya, tidak ada kode produksi yang ditulis tanpa terlebih dahulu menulis unit test yang gagal. Tes kecil. Tes dilakukan secara otomatis. Mengemudi uji adalah logis, yaitu alih-alih menyelami kode produksi (meninggalkan pengujian untuk nanti), praktisi TDD mengekspresikan perilaku kode yang diinginkan dalam pengujian. Setelah tes gagal, praktisi TDD menulis kode, membuat tes lulus. Inti dari proses TDD, ada siklus berulang yang terdiri dari langkah-langkah singkat yang dikenal sebagai “siklus mikro TDD”.
Langkah-langkah siklus TDD dalam daftar berikut didasarkan pada buku 'Test-Driven Development for Embedded C' karya James Grenning:
Tambahkan tes kecil.
Jalankan semua pengujian dan jika yang baru gagal, ia bahkan tidak dapat dikompilasi.
Buat perubahan kecil yang diperlukan untuk lulus ujian.
Jalankan semua tes dan buktikan apakah yang baru lolos.
Memfaktorkan ulang untuk menghapus duplikasi dan meningkatkan ekspresi.
Mari gunakan diagram pada Gambar 1 untuk menemukan cara yang lebih sederhana untuk mengembangkan state machine menggunakan TDD. Saat mesin negara diinisialisasi, itu dimulai dari StateA negara. Setelah menerima Alpha peristiwa, mesin status bertransisi ke StateB state dengan mengeksekusi tindakan xStateA(), effect() dan nStateB() dalam urutan itu. Jadi, bagaimana seseorang dapat menguji SM dari Gambar 1 untuk menentukan apakah ia berperilaku dengan tepat?
Gambar 1. Mesin keadaan dasar (Sumber:VortexMakes)
Cara paling tradisional dan paling sederhana untuk menguji SM seperti Gambar 1 terutama terdiri dari memverifikasi tabel transisi status SMUT (Mesin Status Sedang Diuji). Ini menjadikan kasus uji per negara bagian , di mana SMUT dirangsang oleh peristiwa yang menarik untuk memverifikasi transisi mana yang dipicu. Pada saat yang sama, ini akan berarti memeriksa status target dan tindakan yang dijalankan untuk setiap transisi yang diaktifkan. Jika suatu tindakan cukup kompleks, lebih cocok untuk membuat kasus uji khusus untuk itu. (Artikel Menguji mesin status menggunakan unit test menjelaskan strategi ini secara mendalam).
Setiap kasus uji dibagi menjadi empat fase berbeda sesuai dengan pola xUnit:
Penyiapan menetapkan prasyarat pengujian, seperti status SM saat ini (StateA ), acara yang akan diproses (Alpha ), dan hasil pengujian yang diharapkan, yang merupakan status target transisi (StateB ) dan daftar tindakan yang akan dieksekusi (xStateA(), effect() dan nStateB()).
Olahraga merangsang mesin negara dengan Alpha acara.
Verifikasi memeriksa hasil yang diperoleh.
Pembersihan mengembalikan mesin negara yang sedang diuji ke keadaan awal setelah pengujian. Ini opsional.
Strategi yang disebutkan di atas sudah cukup untuk mengembangkan SM menggunakan TDD. Namun, dalam beberapa kasus lebih dari satu transisi diperlukan untuk memeriksa fungsionalitas. Ini karena efeknya hanya terlihat karena rantai tindakan transisi berikutnya, yang berarti bahwa fungsionalitas melibatkan serangkaian status, peristiwa, dan transisi SMUT. Dalam kasus ini, lebih tepat untuk menguji skenario yang lengkap dan fungsional daripada transisi keadaan terisolasi. Akibatnya, kasus uji lebih fungsional dan kurang abstrak daripada strategi yang disebutkan sebelumnya.
Mari gunakan state machine pada Gambar 2 untuk mengeksplorasi konsep ini.
Gambar 2. Mesin status DoWhile (Sumber:VortexMakes)
Gambar 2 menunjukkan mesin keadaan yang disebut DoWhile, yang memodelkan loop eksekusi yang mirip dengan 'do-while'. DoWhile digambar menggunakan Yakindu Statechart Tool. Tindakan 'x =0' dan 'output =0' dipanggil saat DoWhile dibuat dan tindakan ini menetapkan nilai default semua atribut DoWhile. Jumlah iterasi loop harus diatur melalui 'x++' atau 'x =(x> 0) ? x–:aksi x. Tindakan 'i =0' menetapkan kondisi awal untuk loop. Tubuh loop dieksekusi oleh tindakan 'i++', yang menjaga iterasi loop, kemudian kondisi terminasi dievaluasi oleh pseudostate pilihan melalui penjaga 'i ==x'. Jika benar, badan perulangan dievaluasi lagi, dan seterusnya. Ketika kondisi terminasi menjadi salah, loop akan berhenti mengeksekusi aksi 'output =i'.
Akan sangat membantu untuk membuat daftar pengujian sebelum mengembangkan fungsionalitas baru. Daftar tes berasal dari spesifikasi dan mendefinisikan visi terbaik tentang apa yang harus dilakukan. Karena tidak perlu sempurna, daftar sebelumnya hanyalah dokumen sementara yang dapat dimodifikasi di kemudian hari. Daftar tes awal untuk DoWhile ditunjukkan di bawah ini:
Semua data disetel secara default setelah SM diinisialisasi
Meningkatkan atribut X
Mengurangi atribut X
Satu loop iterasi dapat dieksekusi
Perulangan beberapa iterasi dapat dijalankan
Perulangan non iterasi dapat dijalankan
Periksa nilai di luar batas
Untuk mengembangkan mesin negara DoWhile, Ceedling dan Unity akan digunakan bersama dengan teknik pemrograman yang paling sederhana namun jelas:penggunaan kalimat 'switch-case'. Ceedling adalah sistem pembangunan untuk menghasilkan seluruh pengujian dan lingkungan pembangunan untuk proyek C; Unity adalah alat uji bahasa C ekspresif portabel yang ringan untuk proyek C.
Dua file mewakili mesin status ini, DoWhile.h dan DoWhile.c, jadi mereka adalah kode sumber yang sedang diuji. Daftar Kode 1 menunjukkan sebuah fragmen dari file test_DoWhile.c, yang mengimplementasikan daftar pengujian di atas dengan menerapkan strategi yang disebutkan sebelumnya. Agar artikel ini tetap sederhana, Daftar Kode 1 hanya menampilkan kasus pengujian: 'Satu loop iterasi dapat dieksekusi', yang diimplementasikan oleh test_SingleIteration(). Kode dan model tersedia di https://github.com/leanfrancucci/sm-tdd.git repositori.
Daftar Kode 1:Tes iterasi tunggal (Sumber:VortexMakes)
Tes ini memverifikasi bahwa DoWhile dapat menjalankan hanya satu iterasi dengan benar. Untuk melakukannya, test_SingleIteration() menginisialisasi mesin status DoWhile dengan memanggil DoWhile_init() (baris 96). Ini menetapkan nomor iterasi nol untuk dieksekusi oleh loop DoWhile. Setelah itu, DoWhile siap memproses event dengan memanggil DoWhile_dispatch(). Untuk menjalankan satu iterasi, test_SingleIteration() mengirimkan Naik acara ke DoWhile (baris 97). Acara ini menambah jumlah iterasi menjadi satu. Pengujian memulai loop dengan mengirimkan Start event (baris 98), kemudian mengirimkan Alpha event sehingga DoWhile mengeksekusi satu iterasi (baris 99). Ini diperiksa dengan memverifikasi bahwa nilai atribut out sama dengan jumlah iterasi yang dieksekusi (baris 101). Akhirnya, DoWhile harus tetap berada di StateC negara bagian (baris 102).
Untuk membuktikan bahwa DoWhile dapat mengeksekusi lebih dari satu iterasi, test_SingleIteration() diperluas seperti yang ditunjukkan pada Daftar Kode 2.
Daftar Kode 2:Uji iterasi ganda (Sumber:VortexMakes)
Tes test_NoneIteration() yang ditampilkan dalam Daftar Kode 3 memeriksa bahwa DoWhile tidak menjalankan iterasi apa pun saat menerima Alpha acara tanpa sebelumnya menetapkan nomor iterasi melalui Naik acara.
Daftar Kode 3:Uji non iterasi (Sumber:VortexMakes)
Meskipun detail implementasi DoWhile bukanlah tujuan artikel ini, Daftar Kode 4 dan Daftar Kode 5 menunjukkan bagian dari file DoWhile.c dan DoWhile.h. File-file ini sebenarnya mewakili implementasi demonstratif DoWhile menggunakan kalimat 'switch-case' di C.
Daftar Kode 4:Fragmen implementasi DoWhile (Sumber:VortexMakes)
Daftar Kode 5:Fragmen spesifikasi DoWhile (Sumber:VortexMakes)
Kedua strategi yang diperkenalkan di atas menyediakan metode sederhana dan teratur untuk mengembangkan perangkat lunak mesin negara menggunakan TDD — salah satu pendekatan terpenting untuk meningkatkan kualitas perangkat lunak.
Strategi pertama terutama terdiri dari memverifikasi tabel transisi status SMUT. Metode ini membuat test case per state . Strategi lain mengusulkan untuk mewujudkan kasus uji untuk skenario yang lengkap dan fungsional , yang sering kali melibatkan serangkaian status, peristiwa, dan tindakan SMUT. Strategi kedua ini membuat tes lebih fungsional dan kurang abstrak daripada yang pertama. Meskipun strategi ini tidak bergantung pada jenis sistem, bahasa pemrograman, atau alat tertentu, strategi ini sangat berguna dalam sistem tertanam, karena banyak di antaranya memiliki perilaku berbasis status yang biasanya didefinisikan dalam satu atau lebih mesin status.
Bahasa C dipilih karena merupakan salah satu bahasa yang paling populer untuk pengembangan perangkat lunak tertanam. Jadi, untuk menerapkan TDD dalam bahasa itu, Ceedling dan Unity dipilih. Sebagai kesimpulan, metode ini jelas memungkinkan pengembang untuk membangun dengan cara yang lebih sederhana dan teratur, perangkat lunak yang jauh lebih fleksibel, dapat dipelihara, dan dapat digunakan kembali daripada pendekatan tradisional.