Bagaimana cara menulis unit maintainable, not rapuh, untuk GUI?

16

Saya mencoba menulis tes unit UI untuk aplikasi GUI saya dan saya menghadapi masalah itu, ketika mereka bekerja dengan baik ketika saya awalnya menulis mereka, mereka ternyata rapuh dan mereka pecah setiap kali perubahan desain (yaitu, cukup sering). Saya berjuang untuk menemukan serangkaian pedoman yang akan menuntun saya untuk memiliki unit test yang dapat dipertahankan untuk GUI.

Untuk saat ini, satu hal yang saya temukan adalah tes yang mengatakan "komponen ini harus menunjukkan data inputnya di suatu tempat" adalah baik (dan itu sangat mudah dengan HTML). Tes yang memeriksa keadaan spesifik dari bagian tertentu dari komponen biasanya rapuh. Pengujian akan seperti klik-klik-klik-harapkan, yang mencoba mengikuti perilaku pengguna dan logika bisnis yang mendasarinya (yang merupakan bagian paling penting) biasanya berubah menjadi rapuh. Bagaimana cara menulis tes yang bagus?


Untuk lebih tepatnya, saya ingin tahu beberapa pola tentang apa yang bisa saya uji di UI saya, bukan bagaimana cara mengujinya. Konvensi penamaan dan pengidentifikasi tetap baik, tetapi tidak menyelesaikan masalah inti, yaitu, bahwa GUI banyak berubah. Saya ingin menguji perilaku yang paling tidak mungkin berubah. Bagaimana cara menemukan hal yang benar untuk diuji?

mik01aj
sumber
1
@MichaelDurrant: Anda mengedit pertanyaan tentang pengujian UI secara umum, sementara saya awalnya bertanya tentang pengujian unit. Saya menemukan tes integrasi lebih sulit untuk dipertahankan dan saya lebih suka tes unit daripada mereka, bila memungkinkan.
mik01aj
2
Saya pikir ini adalah bagian dari masalah, pada dasarnya Anda tidak dapat benar-benar menguji antarmuka apa pun dengan pengujian unit saja karena alasan mereka adalah antarmuka ke sesuatu. GUI tidak berbeda dalam hal ini.
jk.
m01, Anda bisa mengubahnya kembali. Saya menganggap tes UI sebagai tes terintegrasi. Tes cenderung mengandalkan data seed dan fixture yang ada dan UI bekerja dengannya. Jika Anda memiliki tes UI sejati yang tidak bergantung pada data lain yang luar biasa. Namun saya menemukan ini relatif jarang.
Michael Durrant
2
terkait tetapi bukan duplikat karena ini tentang tes gui: programmers.stackexchange.com/questions/109703/…
k3b

Jawaban:

3

Masalah umum dengan tes GUI ... Alasan utama tes ini dianggap rapuh adalah karena mereka tidak dapat bertahan dari perubahan dalam GUI yang bukan merupakan perubahan dalam persyaratan . Anda harus berusaha untuk menyusun kode tes Anda sedemikian rupa sehingga perubahan dalam GUI diisolasi ke satu tempat dalam tes.

Sebagai contoh, pertimbangkan tes yang diucapkan sebagai:

Ketika pengguna memasukkan '999' ke dalam bidang nomor telepon dan mengklik tombol simpan, sembulan pesan kesalahan harus mengatakan 'nomor telepon tidak valid'.

Banyak ruang di sini untuk pengujian ini untuk dipecahkan ketika Anda mengerjakan ulang antarmuka, bahkan jika persyaratan untuk validasi tetap ada.

Sekarang, mari kita letakkan ini ke dalam sedikit alternatif kata-kata:

Ketika pengguna memasukkan '999' sebagai nomor telepon dan menyimpan halaman profil, itu akan menunjukkan kesalahan yang mengatakan 'nomor telepon tidak valid'

Tesnya sama, persyaratannya sama, tetapi tes semacam ini akan bertahan dari perubahan untuk UI Anda. Anda perlu mengubah kode, tentu saja, tetapi kode tersebut akan terisolasi. Bahkan jika Anda memiliki sepuluh atau dua puluh tes seperti itu untuk halaman profil Anda dan Anda memindahkan logika validasi errormessage-displaying Anda dari javascript-alert ke jquery-popups misalnya, Anda hanya perlu mengubah bagian uji tunggal yang memeriksa pesan kesalahan.

JDT
sumber
4

Ini adalah masalah umum. Saya akan memperhatikan:

  • Bagaimana Anda menyebutkan elemen

    Gunakan id atau kelas css untuk mengidentifikasi elemen. Favour menggunakan ID CSS saat objek unik. Pertimbangkan kerangka kerja yang Anda gunakan, misalnya dengan Ruby on Rails, nameatribut ditetapkan secara otomatis dan dapat (non-intuitif) lebih baik daripada menggunakan css id atau kelas

  • Bagaimana Anda mengidentifikasi elemen.

    Hindari pengidentifikasi posisi seperti yang table/tr/td/tdmendukung formulir seperti td[id="main_vehicle"atau td[class='alternates']. Pertimbangkan untuk menggunakan atribut data bila perlu. Bahkan lebih baik mencoba untuk menghindari tag tata letak seperti <td>semuanya jadi untuk di atas Anda bisa menambahkan span dan menggunakannya, misalnya <span id="main_vehicle">atau pemilih wildcard seperti di *[id="main_vehicle"]mana *sekarang bisa menjadi div, span, td, dll.

  • Menggunakan uji atribut data spesifik yang hanya digunakan untuk qa dan pengujian.

  • Hindari kualifikasi yang tidak perlu untuk elemen. Anda mungkin menemukan diri Anda menggunakan yang berikut ini:

    body.main div#vehicles > form#vehicle input#primary_vehicle_name

    Namun ini mengharuskan bidang input untuk tetap dalam bentuk dengan id kendaraan yang tepat dan pada halaman dengan tubuh yang memiliki kelas utama dan div dengan id kendaraan yang memiliki anak langsung dari bentuk dengan id dari kendaraan. Setiap perubahan pada salah satu dari struktur itu dan tes rusak Dalam hal ini Anda mungkin menemukan itu

    input#primary_vehicle_name

    cukup untuk mengidentifikasi elemen secara unik.

  • Hindari tes yang merujuk pada teks yang terlihat. Teks pada halaman yang ditampilkan kepada pengguna biasanya berubah seiring berjalannya waktu ketika situs tersebut dikelola dan diperbarui, jadi gunakan pengidentifikasi seperti css id dan css class atau atribut data. Elemen seperti form, inputdan selectdigunakan dalam bentuk juga merupakan bagian yang baik dari elemen pengidentifikasi, biasanya dalam kombinasi dengan id atau kelas, misalnya li.vehicleatau input#first-vehicle Anda juga dapat menambahkan pengidentifikasi Anda sendiri, misalnya <div data-vehicle='dodge'>. Dengan cara ini Anda dapat menghindari penggunaan ID elemen atau kelas, yang kemungkinan akan diubah oleh pengembang dan perancang. Saya sebenarnya menemukan dari waktu ke waktu bahwa lebih baik bekerja dengan pengembang dan desainer dan mencapai kesepakatan atas nama dan cakupan. Itu susah.

  • Bagaimana data tetap dipertahankan.

    Mirip dengan mengidentifikasi elemen yang sebenarnya, cobalah untuk menghindari nilai identifikasi selektor kode in-line yang mendukung objek halaman - potongan kecil teks yang disimpan dalam variabel atau metode dan dengan demikian dapat digunakan kembali dan juga dipelihara secara terpusat. Contoh variabel javascript yang mengikuti pola ini untuk nilai hard-coded:

    storedVars["eqv_auto_year"] = "2015";
    storedVars["eqv_auto_make_1"] = "ALFA ROMEO";
    storedVars["eqv_auto_make_2"] = "HONDA";`  
    

    Lebih banyak pada objek halaman di selenium wiki dan selenium docs

  • Komunikasi dengan pengembang.

    Terlepas dari pendekatan teknis dalam hal 'pengembang melakukan perubahan dan memutus otomatisasi QA' yang merupakan masalah alur kerja. Anda perlu memastikan bahwa: semua orang adalah satu tim yang sama; pengembang menjalankan tes terintegrasi yang sama; standar disepakati dan diikuti oleh kedua kelompok; definisi yang dilakukan mencakup menjalankan dan mungkin memperbarui tes UI; pengembang dan pasangan penguji pada rencana pengujian dan keduanya menghadiri perawatan tiket (jika melakukan Agile) dan berbicara tentang pengujian UI sebagai bagian dari perawatan. Anda harus memastikan bahwa pendekatan dan strategi apa pun yang Anda gunakan untuk penamaan dikoordinasikan dengan pengembang aplikasi. Jika Anda tidak masuk ke halaman yang sama, Anda akan menyukai clash over penamaan objek. Beberapa contoh metode objek halaman yang baru-baru ini saya buat untuk proyek ruby:

    def css_email_opt_in_true
      'auto_policy[email_opt_in][value=1]'
    end 
    
    def css_phone_opt_in
      '*[name="auto_policy[phone_opt_in]"]'
    end 
    
    def css_phone_opt_in_true
      'input[name=phone_opt_in][value=true]'
    end 
    
    def css_credit_rating
      'auto_policy[credit_rating]'
    end
    

    Berikut objek halaman yang sama dengan variabel javascript:

    storedVars["css_email_opt_in"] = "css=*[name='auto_policy[email_opt_in]']";
    storedVars["css_phone_opt_in"]="css=*[name='auto_policy[phone_opt_in]']";
    storedVars["css_phone_opt_in_true"]="css=input[name='phone_opt_in'][value=true]";
    storedVars["css_credit_rating"]="css=select[name='auto_policy[credit_rating]']";
    
Michael Durrant
sumber
2
Ini adalah petunjuk yang berguna, dan saya sebenarnya sudah mengikuti sebagian besar dari mereka. Masalah saya adalah, bahwa saya menulis tes untuk beberapa tombol, dan kemudian tombol ini dihapus ketika tindakan yang sama ditangani dari tempat lain. Atau tombol tetap di sana, tetapi label berubah, dan tombol juga menjalankan beberapa tindakan tambahan.
mik01aj
1
Ahh, itu baik untuk diketahui. Ya untuk hal itu saya akan fokus pada alur kerja dan interaksi qa-developer organisasi
Michael Durrant
1
Saya juga memposting info untuk orang lain yang menemukan pertanyaan Anda dan mungkin tidak memiliki semua bagian di tempat yang Anda miliki atau bahkan mungkin tidak tahu tentang mereka.
Michael Durrant
1
Saya telah memperluas poin poin terakhir saya berdasarkan umpan balik Anda.
Michael Durrant
1
Dan saya telah memperbarui bagian tentang penamaan dan mengidentifikasi elemen dan tentang tidak menggunakan teks yang terlihat.
Michael Durrant
3

Alasan mengapa orang mengembangkan hal-hal seperti MVC, MVP dan presenter terlebih dahulu, dan pola desain yang serupa, adalah untuk memisahkan logika bisnis dari antarmuka pengguna.

Jelas, bagian tampilan hanya dapat diuji dengan memulai program, dan memeriksa apa yang ditampilkan - dengan kata lain, itu hanya dapat diuji dalam tes penerimaan.

Di sisi lain, pengujian logika bisnis dapat dilakukan dalam unit test. Dan itu adalah jawaban untuk pertanyaan Anda. Uji semua yang ada dalam model, dan jika Anda bisa dan mau, Anda juga bisa menguji kode pengontrol.


GUI banyak berubah

Itu hanya bisa terjadi ketika Anda mengubah persyaratan. Ketika persyaratan berubah, tidak ada jalan lain, kecuali mengubah kode. Jika Anda berhasil membuat desain dan arsitektur yang baik, perubahan tidak akan menyebar di banyak tempat.

BЈовић
sumber
2
Saya pikir itu poin yang bagus. Mungkin saya hanya melakukan kesalahan MVC :) Btw, saya percaya bahwa mengubah persyaratan tidak dapat dihindari ketika Anda mengembangkan platform web untuk banyak pengguna. Anda tidak tahu bagaimana perilaku pengguna Anda sampai mereka mulai menggunakan GUI.
mik01aj
2

Tes interaksi GUI harus tidak lebih atau kurang rapuh daripada jenis tes lainnya. Itu adalah; jika aplikasi Anda berubah dalam beberapa cara, tes perlu diperbarui untuk mencerminkan hal itu.

Sebagai perbandingan:

Tes Unit

Asli :validateEmail() harus melempar InvalidDatapengecualian. Yang tercakup dengan benar dalam unit test Anda.

Ubah : validateEmail()harus melemparInvalidEmail pengecualian. Sekarang tes Anda salah, Anda memperbaruinya, dan semuanya berwarna hijau lagi.

Tes GUI

Asli : Memasukkan email yang tidak valid akan menghasilkan kotak kesalahan sembulan yang berisi "Data yang dimasukkan tidak valid". Terdeteksi dengan benar oleh tes Anda.

Ubah : Memasukkan email yang tidak valid akan menghasilkan kesalahan sebaris yang berisi "Email yang dimasukkan salah". Sekarang tes Anda salah, Anda memperbaruinya, dan semuanya berwarna hijau lagi.


Ingatlah bahwa Anda menguji input dan output - beberapa perilaku yang didefinisikan dengan baik. Terlepas dari apakah itu tes GUI atau tes Unit atau tes Integrasi, dll.

Jess Telford
sumber