Uji pengembangan pertama dengan FitNesse

Selama beberapa tahun terakhir, saya telah bekerja di semua peran proses pengujian, menggunakan JavaScript sisi server, Perl, PHP, Struts, Swing, dan arsitektur berbasis model. Semua proyek berbeda, tetapi semuanya memiliki beberapa kesamaan: tenggat waktu terlambat, dan proyek mengalami kesulitan mencapai apa yang benar - benar dibutuhkan pelanggan .

Setiap proyek memiliki beberapa persyaratan, beberapa sangat rinci, beberapa hanya beberapa halaman. Persyaratan tersebut biasanya melalui tiga tahap:

  • Mereka ditulis (baik oleh pelanggan atau oleh kontraktor) dan menerima semacam penerimaan resmi
  • Para penguji mencoba untuk bekerja dengan persyaratan dan menemukan bahwa mereka kurang lebih tidak memadai
  • Proyek memasuki fase pengujian penerimaan, dan pelanggan tiba-tiba teringat semua jenis hal yang perlu dilakukan perangkat lunak tambahan / berbeda

Fase terakhir menyebabkan perubahan, yang menyebabkan tenggat waktu terlewat, yang memberi tekanan pada pengembang, yang pada gilirannya menyebabkan lebih banyak kesalahan. Jumlah bug mulai meningkat dengan cepat, dan kualitas keseluruhan sistem menurun. Terdengar akrab?

Mari pertimbangkan apa yang salah dalam proyek yang dijelaskan di atas: Pelanggan, pengembang, dan penguji tidak bekerja sama; mereka lulus persyaratan, tetapi setiap peran memiliki kebutuhan yang berbeda. Selain itu, pengembang biasanya mengembangkan beberapa jenis pengujian otomatis, dan penguji juga mencoba mengotomatiskan beberapa pengujian. Biasanya, mereka tidak dapat mengkoordinasikan pengujian secara memadai, dan banyak item diuji dua kali, sementara yang lain (biasanya bagian yang sulit), tidak sama sekali. Dan pelanggan tidak melihat adanya tes. Artikel ini menjelaskan cara untuk memecahkan masalah ini dengan menggabungkan persyaratan dengan pengujian otomatis.

Masuk FitNesse

FitNesse adalah wiki dengan beberapa fungsi tambahan untuk memicu pengujian JUnit. Jika pengujian ini digabungkan dengan persyaratan, pengujian tersebut berfungsi sebagai contoh konkret, sehingga persyaratan menjadi lebih jelas. Selanjutnya, data pengujian diatur secara logis. Hal terpenting tentang menggunakan FitNesse, bagaimanapun, adalah ide di baliknya, yang berarti bahwa persyaratan ternyata ditulis (sebagian) sebagai tes, membuatnya dapat diuji dan, oleh karena itu, pemenuhannya dapat diverifikasi.

Menggunakan FitNesse, proses pengembangannya akan terlihat seperti ini: Insinyur persyaratan menulis persyaratan dalam FitNesse (bukan Word). Dia mencoba melibatkan pelanggan sebanyak mungkin, tetapi itu biasanya tidak dapat dicapai setiap hari. Penguji mengintip dokumen berulang kali dan mengajukan pertanyaan sulit sejak hari pertama. Karena penguji berpikir secara berbeda, dia tidak berpikir, "Apa yang akan dilakukan perangkat lunak itu?" tetapi "Apa yang mungkin salah? Bagaimana cara memecahkannya?" Pengembang berpikir lebih seperti insinyur persyaratan; dia ingin tahu, "Apa yang harus dilakukan perangkat lunak itu?"

Penguji mulai menulis tesnya lebih awal, ketika persyaratannya bahkan belum selesai. Dan dia menuliskannya ke dalam persyaratan. Tes menjadi bagian tidak hanya persyaratan, tetapi juga proses peninjauan / penerimaan persyaratan, yang memiliki beberapa keuntungan penting:

  • Pelanggan juga memikirkan tentang pengujian. Biasanya, dia bahkan terlibat dalam pembuatannya (Anda mungkin akan terkejut betapa menyenangkannya dia dengan ini).
  • Spesifikasi menjadi jauh lebih rinci dan tepat, karena tes biasanya lebih tepat daripada teks biasa.
  • Berpikir sejak awal tentang skenario nyata, memberikan data pengujian dan menghitung contoh menghasilkan tampilan perangkat lunak yang jauh lebih jelas — seperti prototipe, hanya dengan lebih banyak fungsi.

Akhirnya, persyaratan diserahkan kepada pengembang. Dia memiliki pekerjaan yang lebih mudah sekarang, karena spesifikasinya cenderung tidak berubah dan karena semua contoh yang disertakan. Mari kita lihat bagaimana proses ini mempermudah pekerjaan pengembang.

Menerapkan test-first

Biasanya, bagian tersulit dari memulai pengembangan tes pertama adalah tidak ada yang mau menghabiskan begitu banyak waktu untuk menulis tes, hanya kemudian menemukan cara untuk membuatnya bekerja. Dengan menggunakan proses yang dijelaskan di atas, pengembang menerima pengujian fungsional sebagai bagian dari kontraknya. Tugasnya berubah dari "Bangun apa yang saya inginkan dan Anda selesai, sampai saya memeriksa pekerjaan Anda dan membuat perubahan" menjadi "Buat tes ini berhasil dan Anda selesai". Sekarang setiap orang memiliki gagasan yang lebih baik tentang apa yang harus dilakukan, kapan pekerjaan akan diselesaikan, dan di mana proyek tersebut berdiri.

Tidak semua pengujian tersebut akan diotomatiskan dan tidak semua akan menjadi pengujian unit. Kami biasanya membagi tes ke dalam kategori berikut (detailnya akan mengikuti):

  • Pengujian berdasarkan data yang perlu diterapkan sebagai pengujian unit. Perhitungan adalah contoh tipikal.
  • Tes berdasarkan kata kunci yang mengotomatiskan penggunaan aplikasi. Ini adalah pengujian sistem dan membutuhkan aplikasi untuk dijalankan. Tombol diklik, data dimasukkan, dan halaman / layar yang dihasilkan diperiksa untuk memuat nilai-nilai tertentu. Tim penguji biasanya menerapkan pengujian ini, tetapi beberapa pengembang juga mendapat manfaat darinya.
  • Tes manual. Pengujian ini terlalu mahal untuk diotomatiskan dan kemungkinan kesalahan tidak cukup parah, atau sangat mendasar (halaman awal tidak ditampilkan) sehingga kerusakannya akan segera diketahui.

Ketika saya pertama kali membaca tentang FitNesse pada tahun 2004, saya tertawa dan berkata bahwa itu tidak akan pernah berhasil. Ide untuk menulis tes saya ke wiki yang mengubahnya secara otomatis menjadi tes tampak terlalu tidak masuk akal. Ternyata, saya salah. FitNesse benar-benar sesederhana kelihatannya.

Kesederhanaan ini dimulai dengan penginstalan. Cukup unduh distribusi lengkap FitNesse dan unzip. Dalam diskusi berikut, saya berasumsi Anda telah membuka ritsleting distribusi ke C: \ fitnesse.

Mulai FitNesse dengan menjalankan skrip run.bat( run.shdi Linux) di C: \ fitnesse. Secara default, FitNesse menjalankan server Web pada port 80, tetapi Anda dapat menentukan port yang berbeda, misalnya 81, dengan menambahkan -p 81ke baris pertama di file batch. Hanya itu yang ada untuk itu; Anda sekarang dapat mengakses FitNesse di // localhost: 81.

Pada artikel ini, saya menggunakan FitNesse versi Java di Windows. Namun, contoh tersebut dapat digunakan untuk versi lain (Python, .Net) dan platform juga.

Beberapa tes

Dokumentasi online FitNesse memberikan beberapa contoh sederhana (sebanding dengan contoh uang terkenal dari JUnit) untuk Anda mulai. Mereka baik-baik saja untuk mempelajari cara menggunakan FitNesse, tetapi mereka tidak cukup rumit untuk meyakinkan beberapa orang yang skeptis. Oleh karena itu, saya akan menggunakan contoh dunia nyata dari salah satu proyek terbaru saya. Saya telah menyederhanakan masalah secara signifikan, dan kodenya, tidak diambil langsung dari proyek, ditulis untuk tujuan ilustrasi. Namun, contoh ini seharusnya cukup rumit untuk menunjukkan kekuatan kesederhanaan FitNesse.

Anggaplah kita sedang mengerjakan proyek yang mengimplementasikan perangkat lunak berbasis Java perusahaan yang kompleks untuk perusahaan asuransi besar. Produk tersebut akan menangani seluruh bisnis perusahaan, termasuk pelanggan dan manajemen kontrak serta pembayaran. Sebagai contoh, kami akan melihat sebagian kecil dari aplikasi ini.

Di Swiss, orang tua berhak atas satu tunjangan anak per anak. Mereka hanya menerima tunjangan ini jika keadaan tertentu terpenuhi, dan jumlahnya bervariasi. Berikut ini adalah versi sederhana dari persyaratan ini. Kami akan mulai dengan persyaratan "tradisional" dan memindahkannya ke FitNesse sesudahnya.

Ada beberapa fase tunjangan anak. Klaim dimulai pada hari pertama setiap bulan anak tersebut lahir dan berakhir pada hari terakhir bulan tersebut anak tersebut mencapai batas usia, menyelesaikan masa magangnya, atau meninggal.

Saat mencapai usia 12 tahun, klaim dinaikkan menjadi 190 CHF (simbol mata uang resmi Swiss) mulai hari pertama bulan lahir.

Pekerjaan penuh waktu dan paruh waktu dari orang tua menyebabkan klaim yang berbeda, seperti yang dijelaskan pada Gambar 1.

Tingkat pekerjaan saat ini dihitung berdasarkan kontrak kerja aktif. Kontrak tersebut harus valid, dan jika tanggal akhir ditetapkan, kontrak tersebut harus ditempatkan di "periode aktif". Gambar 2 menunjukkan berapa banyak uang yang menjadi hak orang tua, tergantung pada usia anak.

Peraturan yang mengatur pembayaran ini diadaptasi setiap dua tahun.

Pada pembacaan pertama, spesifikasinya mungkin terdengar tepat, dan pengembang harus dapat mengimplementasikannya dengan mudah. Tapi apakah kita benar-benar yakin dengan syarat batasnya? Bagaimana kami menguji persyaratan ini?

Kondisi batas
Boundary conditions are the situations directly on, above, and beneath the edges of input and output equivalence classes. Experiences show that test cases exploring boundary conditions have a higher payoff than test cases that do not. A typical example is the infamous "one-off" on loops and arrays.

Scenarios can be a great help in finding exceptions and boundary conditions, as they provide a good way to get domain experts to talk about business.

Scenarios

For most projects, the requirements engineer hands the specification to the developer, who studies the requirements, asks some questions, and starts to design/code/test. Afterwards, the developer hands the software to the test team and, after some rework and fixes, passes it on to the customer (who will likely think of some exceptions requiring changes). Moving the text to FitNesse won't change this process; however, adding examples, scenarios, and tests will.

Scenarios are especially helpful for getting the ball rolling during testing. Some examples follow. Answering the question of how much child allowance is to be paid to each will clarify a lot:

  • Maria is a single parent. She has two sons (Bob, 2, and Peter, 15) and works part-time (20 hours per week) as a secretary.
  • Maria loses her job. Later, she works 10 hours per week as a shop assistant and another 5 hours as a babysitter.
  • Paul and Lara have a daughter (Lisa, 17) who is physically challenged and a son (Frank, 18) who is still in university.

Just talking through these scenarios should help the testing process. Executing them manually on the software will almost certainly find some loose ends. Think we can't do that, since we don't have a prototype yet? Why not?

Keyword-driven testing

Keyword-driven testing can be used to simulate a prototype. FitNesse allows us to define keyword-driven tests (see "Totally Data-Driven Automated Testing" for details). Even with no software to (automatically) execute the tests against, keyword-driven tests will help a lot.

Figure 3 shows what a keyword-driven test might look like. The first column represents keywords from FitNesse. The second column represents methods in a Java class (we write those, and they need to follow the restrictions put on method names in Java). The third column represents data entered into the method from the second column. The last row demonstrates what a failed test might look like (passed tests are green). As you can see, it is quite easy to find out what went wrong.

Such tests are easy and even fun to create. Testers without programming skills can create them, and the customer can read them (after a short introduction).

Defining tests this way, right next to the requirement, has some important advantages over the traditional definition of test cases, even without automation:

  • The context is at hand. The test case itself can be written with the least possible amount of work and is still precise.
  • If the requirement changes, there is a strong chance that the test will change as well (not very likely when several tools are used).
  • The test can be executed at once to show what needs to be fixed to make this new/changed requirement work.

To automate the test, a thin layer of software is created, which is delegated to the real test code. These tests are especially useful for automating manual GUI tests. I developed a test framework based on HTTPUnit for automating the testing of Webpages.

Here is the code automatically executed by FitNesse:

package stephanwiesner.javaworld;

import fit.ColumnFixture;

public class ChildAllowanceFixture extends ColumnFixture { public void personButton() { System.out.println("pressing person button"); } public void securityNumber(int number) { System.out.println("entering securityNumber " + number); } public int childAllowance() { System.out.println("calculating child allowance"); return 190; } [...] }

The output of the tests can be examined in FitNesse as well (see Figure 4), which greatly helps with debugging. In contrast to JUnit, where one is discouraged from writing debug messages, I find them absolutely necessary when working with automated Web tests.

When testing a Web-based application, error pages are included in the FitNesse page and displayed, making debugging much easier than working with log files.

Data-driven testing

Meskipun pengujian berbasis kata kunci baik untuk otomatisasi GUI, pengujian berbasis data adalah pilihan pertama untuk menguji kode yang melakukan segala jenis penghitungan. Jika Anda pernah menulis unit test sebelumnya, hal apa yang paling menyebalkan tentang pengujian tersebut? Kemungkinannya adalah, Anda memikirkan data. Pengujian Anda akan penuh dengan data, yang sering berubah, membuat pemeliharaan menjadi mimpi buruk. Menguji kombinasi yang berbeda membutuhkan data yang berbeda, mungkin membuat pengujian Anda rumit, binatang jelek.