Menguji fungsionalitas pengguna situs web dengan objek halaman Capybara

Objek Halaman dapat digunakan sebagai metode abstraksi (isolasi) tes Anda yang kuat dari implementasi teknis. Penting untuk diingat bahwa mereka (Obyek Halaman) dapat digunakan untuk meningkatkan stabilitas tes dan mempertahankan prinsip KERING (jangan ulangi diri Anda sendiri) - dengan merangkum fungsionalitas (situs web) dalam metode sederhana.

Dengan kata lain


Halaman Obyek adalah turunan dari kelas yang abstrak (mengisolasi) antarmuka pengguna dari lingkungan pengujian, menyajikan metode untuk berinteraksi dengan antarmuka pengguna, dan mengekstrak informasi yang diperlukan.

Terminologi


Istilah Halaman Obyek adalah konsep yang terlalu umum. Dalam pengalaman saya, Obyek Halaman mencakup 3 jenis berikut:

  • Objek Komponen mewakili komponen atau widget tertentu di antarmuka pengguna. Misalnya: tabel, menu, artikel, dan blok lain yang berisi sekelompok komponen.
  • Objek Halaman menggambarkan area atau antarmuka pengguna tertentu dalam aplikasi web. Ini dapat terdiri dari beberapa Objek Komponen dan mungkin berisi metode yang mudah untuk berinteraksi dengan abstraksi yang terkandung dalam objek ini.
  • Pengalaman digunakan untuk mengelompokkan fungsi yang kompleks, pengujian yang membutuhkan beberapa langkah, atau interaksi dengan beberapa halaman. Dari pengalaman saya sendiri, saya menggunakan konsep ini untuk mengabstraksi perilaku kompleks pada suatu halaman (menguji halaman pendidikan, membuat pengguna baru, dll.)

Contohnya


Pertimbangkan uji RSpec Capybara yang sederhana, yang membuat blog dan tidak menggunakan objek halaman:

require 'feature_helper' feature 'Blog management', type: :feature do scenario 'Successfully creating a new blog' do visit '/' click_on 'Form Examples' expect(page).to have_content('Create Blog') fill_in 'blog_title', with: 'My Blog Title' fill_in 'blog_text', with: 'My new blog text' click_on 'Save Blog' expect(page).to have_selector('.blog--show') expect(page).to have_content('My Blog Title') expect(page).to have_content('My new blog text') end scenario 'Entering no data' do visit '/' click_on 'Form Examples' expect(page).to have_content('Create Blog') click_on 'Save Blog' expect(page).to have_content('4 errors stopped this form being submitted') expect(page).to have_content("Title can't be blank") expect(page).to have_content("Text can't be blank") expect(page).to have_content('Title is too short') expect(page).to have_content('Text is too short') end end 

Mari kita lihat lebih dekat kode ini, ada beberapa masalah. Ada tindakan berikut: beralih ke halaman yang sesuai, berinteraksi dengan halaman dan memeriksa konten. Bagian dari kode digandakan, tetapi ini dapat diperbaiki dengan mengikuti prinsip KERING .

Penting untuk dipahami bahwa kode ini sulit dipertahankan jika ada perubahan dalam aplikasi yang sedang diuji. Misalnya, kelas elemen, nama, dan pengidentifikasi dapat berubah, yang memerlukan pembaruan berkala dari kode pengujian.

Juga dalam kode ini tidak ada 'konteks semantik', sulit untuk memahami baris kode mana yang secara logis dikelompokkan.

Pengantar Objek Halaman


Seperti yang dibahas di bagian terminologi, Objek Halaman dapat digunakan untuk mewakili abstraksi tingkat presentasi.

Mengambil contoh sebelumnya dan menggunakan Obyek Halaman untuk membuat blog baru dan melihat blog, kita dapat menghapus kode dari contoh sebelumnya.

Setelah menyingkirkan informasi spesifik tentang implementasi teknis, hasil akhir (kode) harus dapat dibaca dan tidak boleh berisi informasi spesifik tentang antarmuka pengguna (id, kelas css, dll.).

 require 'feature_helper' require_relative '../pages/new_blog' require_relative '../pages/view_blog' feature 'Blog management', type: :feature do let(:new_blog_page) { ::Pages::NewBlog.new } let(:view_blog_page) { ::Pages::ViewBlog.new } before :each do new_blog_page.visit_location end scenario 'Successfully creating a new blog' do new_blog_page.create title: 'My Blog Title', text: 'My new blog text' expect(view_blog_page).to have_loaded expect(view_blog_page).to have_blog title: 'My Blog Title', text: 'My new blog text' end scenario 'Entering no data' do new_blog_page.create title: '', text: '' expect(view_blog_page).to_not have_loaded expect(new_blog_page).to have_errors "Title can't be blank", "Text can't be blank", "Title is too short", "Text is too short" end end 

Membuat Objek Halaman

Langkah pertama dalam membuat Objek Halaman adalah membuat struktur kelas halaman dasar :

 module Pages class NewBlog include RSpec::Matchers include Capybara::DSL # ... end end 

Menghubungkan (mengaktifkan) Capybara :: DSL untuk memungkinkan instance Objek Halaman menggunakan metode yang tersedia di Capybara

 has_css? '.foo' has_content? 'hello world' find('.foo').click 

Selain itu, saya menggunakan
termasuk RSpec :: Matchers
dalam contoh di atas untuk menggunakan pustaka RSpec harapan .

Jangan melanggar perjanjian, Halaman Obyek tidak harus termasuk ekspektasi (harapan) . Namun, jika perlu, saya lebih suka pendekatan ini mengandalkan mekanisme bawaan Capybara untuk menangani kondisi.

Misalnya, kode Capybara berikut ini akan mengharapkan keberadaan 'foo' di dalam Objek Halaman (dalam hal ini, sendiri ):

 expect(self).to have_content 'foo' 

Namun, dalam kode berikut:

 expect(page_object.content).to match 'foo' 

Kesalahan yang tidak terduga mungkin terjadi (tes mengambang mungkin terjadi), karena page_object.content segera diperiksa untuk kesesuaian dengan kondisi, dan mungkin belum dideklarasikan. Untuk lebih banyak contoh, saya akan merekomendasikan membaca tes integrasi asinkron dengan thinkbot yang dapat diandalkan dengan Capybara .

Metode Pembuatan


Kita dapat abstrak (menggambarkan) tempat (wilayah) dari mana kita ingin mendapatkan data, dalam kerangka satu metode:

 def visit_location visit '/blogs/new' # It can be beneficial to assert something positive about the page # before progressing with your tests at this point # # This can be useful to ensures that the page has loaded successfully, and any # asynchronous JavaScript has been loaded and retrieved etc. # # This is required to avoid potential race conditions. expect(self).to have_loaded end def has_loaded? self.has_selector? 'h1', text: 'Create Blog' end 

Penting untuk memilih nama yang benar secara semantik untuk metode untuk Objek Halaman Anda

 def create(title:, text:) # ... end def has_errors?(*errors) # ... end def has_error?(error) # ... end 

Secara umum, penting untuk mengikuti prinsip metode yang terintegrasi secara fungsional dan, jika mungkin, mematuhi prinsip tanggung jawab tunggal (Prinsip Tanggung Jawab Tunggal).

Objek komponen


Dalam contoh kami, kami menggunakan kelas NewBlog, tetapi tidak ada implementasi untuk dibuat.

Karena kami berinteraksi dengan formulir, kami juga dapat memperkenalkan kelas untuk mewakili komponen ini:

 # ... def create(title:, text:) blog_form.new.create title: title, text: text end # ... private def blog_form ::Components::BlogForm end 

Di mana metode implementasi untuk BlogForm dapat disembunyikan:

 module Components class BlogForm include RSpec::Matchers include Capybara::DSL def create(title:, text:) within blog_form do fill_in 'blog_title', with: title fill_in 'blog_text', with: text click_on 'Save Blog' end end private def blog_form find('.blog--new') end end end 

Semuanya bersama


Menggunakan kelas-kelas di atas, Anda sekarang dapat meminta dan membuat instance Obyek Halaman dari halaman Anda sebagai bagian dari deskripsi objek.

 require 'feature_helper' require_relative '../pages/new_blog' require_relative '../pages/view_blog' feature 'Blog management', type: :feature do let(:new_blog_page) { ::Pages::NewBlog.new } let(:view_blog_page) { ::Pages::ViewBlog.new } # ... end 

Catatan: Saya sengaja membuat objek halaman secara manual di bagian atas file objek. Dalam beberapa tes RSpec mungkin lebih mudah untuk secara otomatis mengunduh semua file dukungan dan memberikan akses kepada mereka dalam file objek, namun, ini dapat menyebabkan beban kerja yang berlebihan ketika menggunakan potongan kode yang besar. Secara khusus, ini akan menyebabkan startup lambat dan potensi ketergantungan siklus yang tidak diinginkan.

Panggil Objek Halaman


Sekarang di setiap skenario, kita akan memiliki akses ke instance new_blog_page dan view_blog_page :

 scenario 'Successfully creating a new blog' do new_blog_page.create title: 'My Blog Title', text: 'My new blog text' expect(view_blog_page).to have_loaded expect(view_blog_page).to have_blog title: 'My Blog Title', text: 'My new blog text' end 

Konvensi Penamaan / Metode Predikat


Seperti kebanyakan hal dalam Rails / Ruby, ada beberapa konvensi yang mungkin tampak sepele (tidak mengikat) sepenuhnya.

Dalam pengujian kami, kami berinteraksi dengan objek halaman menggunakan has_loaded dan have_blog :

 expect(view_blog_page).to have_loaded expect(view_blog_page).to have_blog title: 'My Blog Title', text: 'My new blog text' 

Namun, nama metode objek halaman kami sebenarnya telah di- load? dan has_blog? :

 def has_loaded? # ... end def has_blog?(title:, text:) # ... end 

Ini adalah perbedaan halus yang perlu diperhatikan. Untuk informasi lebih lanjut tentang konvensi ini, saya akan merekomendasikan membaca tautan pencocokan predikat berikut.

Git, kode sumber yang digunakan dalam contoh
Asli

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


All Articles