Cara membangun piramida di trunk atau aplikasi Development-Driven Development di Spring Boot

Kerangka Pegas sering dikutip sebagai contoh kerangka kerja Cloud Native , yang dirancang untuk bekerja di cloud, mengembangkan aplikasi Dua Belas-Faktor , layanan mikro, dan salah satu yang paling stabil, tetapi pada saat yang sama produk-produk inovatif. Tetapi dalam artikel ini saya ingin membahas satu sisi yang lebih kuat dari Spring: itu adalah dukungan pengembangannya melalui pengujian (kemampuan-TDD?). Terlepas dari konektivitas TDD, saya sering memperhatikan bahwa proyek Spring mengabaikan beberapa praktik terbaik untuk pengujian, menciptakan sepeda motor mereka sendiri, atau tidak menulis tes sama sekali karena mereka "lambat" atau "tidak dapat diandalkan." Dan saya akan memberi tahu Anda cara menulis tes cepat dan andal untuk aplikasi pada Kerangka Kerja Musim Semi dan melakukan pengembangan melalui pengujian. Jadi, jika Anda menggunakan Spring (atau ingin memulai), pahami tes apa yang secara umum (atau ingin dipahami), atau berpikir bahwa contextLoads adalah level yang diperlukan dan tingkat pengujian integrasi yang cukup - itu akan menarik!


Kemampuan "TDD" sangat ambigu, dan tidak dapat diukur, tetapi bagaimanapun, Spring memiliki banyak hal yang oleh desain membantu untuk menulis integrasi dan pengujian unit dengan upaya minimal. Sebagai contoh:


  • Pengujian integrasi - Anda dapat dengan mudah meluncurkan aplikasi, mengunci komponen, mendefinisikan kembali parameter, dll.
  • Pengujian integrasi fokus - hanya akses ke data, hanya web, dll.
  • Dukungan di luar kotak - dalam-memori memori, antrian pesan, otentikasi dan otorisasi dalam pengujian
  • Menguji melalui kontrak (Kontrak Spring Cloud)
  • Dukungan Pengujian Web UI Menggunakan HtmlUnit
  • Fleksibilitas konfigurasi aplikasi - profil, konfigurasi pengujian, komponen, dll.
  • Dan masih banyak lagi

Untuk mulai dengan, pengantar kecil tapi perlu tentang TDD dan pengujian secara umum.


Pengembangan yang digerakkan oleh tes


TDD didasarkan pada ide yang sangat sederhana - kami menulis tes sebelum kami menulis kode. Secara teori, ini terdengar menakutkan, tetapi setelah beberapa waktu pemahaman tentang praktik dan teknik muncul, dan pilihan untuk menulis tes sesudahnya menyebabkan ketidaknyamanan yang nyata. Salah satu praktik utama adalah iterasi , mis. membuat semuanya menjadi iterasi yang kecil dan terfokus, yang masing-masing dideskripsikan sebagai Red-Green-Refactor .


Pada fase merah , kami menulis tes jatuh, dan sangat penting bahwa jatuh dengan alasan dan deskripsi yang jelas, dapat dimengerti, dan bahwa tes itu sendiri selesai dan lulus ketika kode ditulis. Tes harus memeriksa perilaku , bukan implementasinya , mis. ikuti pendekatan black box, maka saya akan menjelaskan alasannya.


Pada fase hijau , kami menulis kode minimum yang diperlukan untuk lulus tes. Kadang-kadang menarik untuk berlatih dan membuatnya se-gila mungkin (walaupun lebih baik tidak terbawa) dan ketika suatu fungsi mengembalikan boolean tergantung pada kondisi sistem, "operan" pertama mungkin hanya return true .


Pada fase refactoring , yang hanya dapat dimulai ketika semua tes berwarna hijau , kami akan melakukan refactor kode dan membawanya ke kondisi yang tepat. Itu bahkan tidak perlu untuk sepotong kode yang kami tulis, karena itu penting untuk memulai refactoring pada sistem yang stabil. Pendekatan "kotak hitam" hanya akan membantu refactoring, mengubah implementasi, tetapi tidak menyentuh perilaku.


Saya akan berbicara tentang berbagai aspek TDD di masa depan, setelah semua, ini adalah ide dari serangkaian artikel, jadi sekarang saya tidak akan terlalu memikirkan detailnya. Tetapi sebelum menanggapi kritik TDD standar, saya akan menyebutkan beberapa mitos yang sering saya dengar.


  • "TDD adalah sekitar 100% cakupan kode, tetapi tidak memberikan jaminan" - pengembangan melalui pengujian tidak ada hubungannya dengan cakupan 100% sama sekali. Di banyak tim tempat saya bekerja, metrik ini bahkan tidak diukur, dan diklasifikasikan sebagai metrik kesombongan. Dan ya, cakupan tes 100% tidak ada artinya.
  • "TDD hanya berfungsi untuk fungsi-fungsi sederhana, aplikasi nyata dengan database dan keadaan sulit tidak dapat dibuat dengan itu" adalah alasan yang sangat populer, biasanya dilengkapi dengan "Kami memiliki aplikasi yang rumit sehingga kami tidak menulis tes sama sekali, Anda tidak dapat melakukannya sama sekali." Saya melihat pendekatan TDD yang berfungsi pada aplikasi yang sepenuhnya berbeda - web (dengan dan tanpa SPA), seluler, API, layanan mikro, monolit, sistem perbankan kompleks, platform cloud, kerangka kerja, platform ritel yang ditulis dalam berbagai bahasa dan teknologi. Jadi mitos populer "Kami unik, semuanya berbeda" adalah alasan yang paling sering untuk tidak menginvestasikan usaha dan uang dalam pengujian, tetapi bukan alasan nyata (meskipun mungkin juga ada alasan nyata).
  • "Masih akan ada bug dengan TDD" - tentu saja, seperti pada perangkat lunak lain. TDD bukan tentang bug atau ketidakhadiran mereka sama sekali, ini adalah alat pengembangan. Suka debugging. Seperti IDE. Suka dokumentasinya. Tidak satu pun dari alat ini yang menjamin tidak adanya bug, mereka hanya membantu mengatasi meningkatnya kompleksitas sistem.

Tujuan utama TDD dan umumnya pengujian adalah untuk memberikan kepercayaan tim bahwa sistem bekerja dengan stabil. Oleh karena itu, tidak ada praktik pengujian yang menentukan berapa banyak dan tes mana yang harus ditulis. Tulis seberapa banyak yang Anda pikir perlu, seberapa banyak Anda perlu memastikan bahwa saat ini kode dapat dimasukkan ke dalam produksi dan akan berfungsi . Ada orang yang menganggap tes integrasi cepat sebagai kotak hitam ultimatif yang diperlukan dan memadai, dan tes unit opsional. Seseorang mengatakan bahwa tes e2e dengan kemungkinan rollback cepat ke versi sebelumnya dan keberadaan rilis kenari tidak begitu penting. Berapa banyak tim - begitu banyak pendekatan, penting untuk menemukan Anda sendiri.

Salah satu tujuan saya adalah untuk beralih dari format "pengembangan melalui pengujian fungsi yang menambah dua angka" dalam cerita TDD dan melihat aplikasi nyata, semacam praktik pengujian yang telah diuapkan ke aplikasi minimum, dikumpulkan pada proyek nyata. Sebagai contoh semi-nyata, saya akan menggunakan aplikasi web kecil yang saya buat sendiri untuk abstrak pabrik Bakery - Pabrik Kue . Saya berencana untuk menulis artikel kecil, setiap kali memfokuskan pada bagian fungsionalitas aplikasi yang terpisah dan menunjukkan melalui TDD Anda dapat merancang API, struktur internal aplikasi dan mempertahankan refactoring yang konstan.


Contoh rencana untuk serangkaian artikel, seperti yang saya lihat saat ini, adalah:


  1. Walking skeleton - framework aplikasi tempat Anda dapat menjalankan siklus Red-Green-Refactor
  2. Desain UI Pengujian dan Perilaku Didorong
  3. Pengujian Akses Data (Data Musim Semi)
  4. Otorisasi dan pengujian otentikasi (Spring Security)
  5. Jet Stack (WebFlux + Project Reactor)
  6. Interoperabilitas layanan (mikro) dan kontrak (Spring Cloud)
  7. Menguji Antrian Pesan (Awan Musim Semi)

Artikel pengantar ini akan membahas poin 1 dan 2 - Saya akan membuat kerangka kerja aplikasi dan tes UI dasar menggunakan BDD - atau pendekatan pengembangan yang didorong oleh perilaku . Setiap artikel akan dimulai dengan cerita pengguna , tetapi saya tidak akan berbicara tentang bagian "produk" untuk menghemat waktu. Cerita pengguna akan ditulis dalam bahasa Inggris, itu akan segera menjadi jelas mengapa. Semua contoh kode dapat ditemukan di GitHub, jadi saya tidak akan melewatkan semua kode, hanya bagian-bagian penting.


Cerita pengguna adalah deskripsi fitur aplikasi bahasa alami yang biasanya ditulis atas nama pengguna sistem.

Kisah pengguna 1: Pengguna melihat halaman sambutan


Sebagai Alice, pengguna baru
Saya ingin melihat halaman selamat datang ketika mengunjungi situs web Pabrik Kue
Sehingga saya tahu kapan Cake Factory akan diluncurkan

Kriteria penerimaan:
Skenario: pengguna mengunjungi kunjungan situs web sebelum tanggal peluncuran
Mengingat saya pengguna baru
Ketika saya mengunjungi situs web Pabrik Kue
Lalu saya melihat pesan 'Terima kasih atas minat Anda'
Dan saya melihat pesan 'Situs web akan segera hadir ...'

Dibutuhkan pengetahuan: apa itu Pengembangan Perilaku Bergerak dan Mentimun , dasar-dasar Pengujian Boot Musim Semi .


Kisah pengguna pertama cukup mendasar, tetapi tujuannya belum dalam kompleksitas, tetapi dalam menciptakan kerangka berjalan - aplikasi minimal untuk memulai siklus TDD .


Setelah membuat proyek baru pada Spring Initializr dengan modul Web dan Moustache, untuk permulaan saya akan memerlukan beberapa perubahan lagi untuk build.gradle :


  • tambahkan HtmlUnit testImplementation('net.sourceforge.htmlunit:htmlunit') . Anda tidak perlu menentukan versi, plugin manajemen ketergantungan Boot Musim Semi untuk Gradle akan secara otomatis memilih versi yang diperlukan dan kompatibel
  • bermigrasi proyek dari JUnit 4 ke JUnit 5 (karena 2018 ada di halaman)
  • tambahkan dependensi ke Mentimun - perpustakaan yang akan saya gunakan untuk menulis spesifikasi BDD
  • hapus dibuat oleh CakeFactoryApplicationTests default dengan contextLoads tak contextLoads

Pada umumnya, ini adalah "kerangka" dasar aplikasi, Anda sudah dapat menulis tes pertama.


Untuk mempermudah navigasi dalam kode, saya akan berbicara singkat tentang teknologi yang digunakan.


Mentimun


Mentimun adalah kerangka kerja Pembangunan Berbasis Perilaku yang membantu menciptakan "spesifikasi yang dapat dieksekusi", mis. jalankan tes (spesifikasi) yang ditulis dalam bahasa alami. Plugin Cucumber mem-parsing kode sumber di Jawa (dan banyak bahasa lainnya) dan menggunakan definisi langkah untuk menjalankan kode nyata. Definisi langkah adalah metode kelas yang dianotasi oleh @Given , @When , @Then dan anotasi lainnya.


Htmlunit


Halaman beranda proyek menyebut HtmlUnit "browser tanpa GUI untuk aplikasi Java." Tidak seperti Selenium, HtmlUnit tidak meluncurkan browser nyata dan, yang paling penting, tidak membuat halaman sama sekali, bekerja langsung dengan DOM. JavaScript didukung melalui mesin Mozilla Rhino. HtmlUnit sangat cocok untuk aplikasi klasik, tetapi tidak terlalu bersahabat dengan Aplikasi Halaman Tunggal. Untuk memulainya, itu sudah cukup, dan kemudian saya akan mencoba menunjukkan bahwa bahkan hal-hal seperti kerangka kerja pengujian dapat dijadikan bagian dari implementasi, dan bukan fondasi aplikasi.


Tes pertama


Sekarang cerita pengguna yang ditulis dalam bahasa Inggris akan berguna bagi saya. Pemicu terbaik untuk memulai iterasi TDD berikutnya adalah kriteria penerimaan yang ditulis sedemikian rupa sehingga dapat diubah menjadi spesifikasi yang dapat dieksekusi dengan minimum isyarat.


Idealnya, cerita pengguna harus ditulis sehingga hanya dapat disalin ke spesifikasi BDD dan dijalankan. Ini jauh dari selalu sederhana dan tidak selalu mungkin, tetapi ini harus menjadi tujuan pemilik produk dan seluruh tim, meskipun tidak selalu dapat dicapai.

Jadi, fitur pertama saya.


 Feature: Welcome page Scenario: a user visiting the web-site visit before the launch date Given a new user, Alice When she visits Cake Factory web-site Then she sees a message 'Thank you for your interest' And she sees a message 'The web-site is coming in December!' 

Jika Anda membuat uraian langkah ( plugin Intellij IDEA membantu mendukung banyak hal Gherkin) dan menjalankan tes, maka, tentu saja, akan berwarna hijau - belum menguji apa pun. Dan inilah fase penting untuk mengerjakan tes - Anda perlu menulis tes, seolah-olah kode utama ditulis .


Seringkali, bagi mereka yang mulai mengusir TDD, sebuah keadaan pingsan di sini - sulit untuk menempatkan di kepala algoritma dan logika dari sesuatu yang belum ada. Dan oleh karena itu, sangat penting untuk memiliki iterasi sekecil dan fokus mungkin, mulai dari kisah pengguna dan turun ke tingkat integrasi dan unit. Penting untuk fokus pada satu tes pada satu waktu dan mencoba untuk menjadi basah dan mengabaikan dependensi yang belum penting. Saya kadang-kadang memperhatikan bagaimana orang dengan mudah pergi ke samping - membuat antarmuka atau kelas untuk ketergantungan, segera menghasilkan kelas tes kosong untuk itu, satu lagi ketergantungan ditambahkan di sana, antarmuka lain dibuat dan sebagainya.


Jika ceritanya "perlu menyegarkan status saat menyimpan", maka sangat sulit untuk mengotomatisasi dan memformalkan. Dalam contoh saya, setiap langkah dapat ditata dengan jelas dalam urutan langkah-langkah yang dapat dijelaskan oleh kode. Jelas bahwa ini adalah contoh paling sederhana dan tidak menunjukkan banyak, tapi saya berharap lebih jauh, dengan meningkatnya kompleksitas, ini akan menjadi lebih menarik.

Merah


Jadi, untuk fitur pertama saya, saya membuat deskripsi langkah berikut:


 @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) public class WelcomePage { private WebClient webClient; private HtmlPage page; @LocalServerPort private int port; private String baseUrl; @Before public void setUp() { webClient = new WebClient(); baseUrl = "http://localhost:" + port; } @Given("a new user, Alice") public void aNewUser() { // nothing here, every user is new by default } @When("she visits Cake Factory web-site") public void sheVisitsCakeFactoryWebSite() throws IOException { page = webClient.getPage(baseUrl); } @Then("she sees a message {string}") public void sheSeesAMessageThanksForYourInterest(String expectedMessage) { assertThat(page.getBody().asText()).contains(expectedMessage); } } 

Beberapa hal yang perlu diperhatikan:


  • fitur diluncurkan oleh file lain, Features.java menggunakan RunWith annotation from JUnit 4, Cucumber tidak mendukung versi 5, sayangnya
  • @SpringBootTest anotasi ditambahkan ke deskripsi langkah-langkah, cucumber-spring mengambilnya dari sana dan mengkonfigurasi konteks pengujian (mis., Meluncurkan aplikasi)
  • Aplikasi webEnvironment = RANDOM_PORT untuk pengujian dimulai dengan webEnvironment = RANDOM_PORT dan port acak ini diteruskan ke tes menggunakan @LocalServerPort , Spring akan menemukan anotasi ini dan mengatur nilai bidang ke port server

Dan tes, seperti yang diharapkan, lumpuh dengan kesalahan 404 for http://localhost:51517 .


Kesalahan yang menyebabkan crash tes sangat penting, terutama ketika datang ke unit atau tes integrasi, dan kesalahan ini adalah bagian dari API. Jika pengujian lumpuh dengan NullPointerException ini tidak terlalu baik, tetapi BaseUrl configuration property is not set - jauh lebih baik.

Hijau


Untuk membuat tes hijau, saya menambahkan pengendali dasar dan tampilan dengan HTML minimal:


 @Controller public class IndexController { @GetMapping public String index() { return "index"; } } 

 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Cake Factory</title> </head> <body> <h1>Thank you for your interest</h1> <h2>The web-site is coming in December!</h2> </body> </html> 

Tesnya hijau, aplikasinya berfungsi, meskipun dibuat dalam tradisi desain teknik yang parah.


Pada proyek nyata dan dalam tim yang seimbang , tentu saja, saya akan duduk bersama desainer dan kami akan mengubah HTML telanjang menjadi sesuatu yang jauh lebih indah. Namun dalam kerangka artikel itu, keajaiban tidak akan terjadi, sang putri akan tetap menjadi katak.

Pertanyaan "apa bagian dari TDD adalah desain" tidak begitu sederhana. Salah satu praktik yang menurut saya berguna adalah pada awalnya bahkan tidak melihat UI sama sekali (bahkan tidak menjalankan aplikasi untuk menyelamatkan saraf Anda), menulis tes, membuatnya hijau - dan kemudian, memiliki fondasi yang stabil, bekerja di ujung depan, terus memulai kembali tes .


Refactor


Dalam iterasi pertama, tidak ada refactoring tertentu, tetapi meskipun saya menghabiskan 10 menit terakhir memilih template untuk Bulma , yang dapat dihitung sebagai refactoring!


Kesimpulannya


Sementara aplikasi tidak memiliki pekerjaan keamanan, atau database, atau API, maka tes dan TDD terlihat cukup sederhana. Dan secara umum, dari piramida pengujian, saya hanya menyentuh bagian paling atas, tes UI. Tetapi dalam hal ini, sebagian, rahasia dari pendekatan lean adalah melakukan segalanya dalam iterasi kecil, satu komponen pada satu waktu. Ini membantu untuk fokus pada tes, membuatnya sederhana, dan mengontrol kualitas kode. Saya harap di artikel-artikel berikut ini akan lebih menarik.


Referensi



PS Judul artikelnya tidak segila kelihatannya di awal, saya pikir banyak yang sudah menebak. "Cara membuat piramida di boot Anda" adalah referensi ke piramida pengujian (saya akan memberi tahu Anda lebih banyak tentangnya nanti) dan Spring Boot, di mana boot dalam bahasa Inggris Inggris juga berarti "trunk".

Source: https://habr.com/ru/post/id431306/


All Articles