Resolusi Ambiguitas Kapibara

97

Bagaimana cara mengatasi ambiguitas di Kapibara? Untuk beberapa alasan saya memerlukan link dengan nilai yang sama di halaman tetapi saya tidak dapat membuat pengujian karena saya mendapatkan error

Failure/Error: click_link("#tag1")
     Capybara::Ambiguous:
       Ambiguous match, found 2 elements matching link "#tag1"

Alasan mengapa saya tidak bisa menghindari ini adalah karena desainnya. Saya mencoba membuat ulang halaman twitter dengan tweet / tag di sebelah kanan dan tag di kiri halaman. Oleh karena itu, tidak dapat dihindari bahwa halaman link yang identik muncul di halaman yang sama.

neilmarion.dll
sumber
Bisakah Anda memposting beberapa kode juga?
Heena Hussain
8
Anda tidak boleh menetapkan id yang sama ke dua elemen di halaman. Jika Anda akan memiliki tautan yang identik, jangan tetapkan id ke elemen, gunakan kelas sebagai gantinya.
Chris Salzberg

Jawaban:

147

Solusi saya adalah

first(:link, link).click

dari pada

click_link(link)
e-seng
sumber
6
Hal ini dirinci dalam Panduan Peningkatan Kapibara yang mungkin berguna bagi Anda jika mengalami masalah ini.
Ritchie
1
Pada Capybara 2.0 jangan lakukan ini kecuali Anda benar-benar harus melakukannya. Lihat jawaban @ Andrey di bawah dan penjelasan tentang Kecocokan yang Mendua dalam panduan peningkatan yang ditautkan di atas.
jim
4
Secara khusus, Capybara 2.0 memiliki logika menunggu yang cerdas untuk memastikan spesifikasi lulus atau gagal secara konsisten di seluruh mesin dengan kecepatan pemrosesan yang berbeda sementara hanya menunggu waktu minimum yang diperlukan. Menggunakan firstseperti yang disarankan di atas, kecuali Anda benar-benar tahu apa yang Anda lakukan, kemungkinan besar akan menghasilkan spesifikasi yang cocok untuk Anda, tetapi gagal dalam rakitan CI atau pada mesin rekan.
jim
1
Untuk diskusi yang baik lihat: robots.thoughtbot.com/…
jim
74

Perilaku kapibara seperti itu disengaja dan saya percaya itu tidak boleh diperbaiki seperti yang disarankan di sebagian besar jawaban lain.

Versi Capybara sebelum 2.0 mengembalikan elemen pertama alih-alih memunculkan pengecualian tetapi pengelola Capybara kemudian memutuskan bahwa itu adalah ide yang buruk dan lebih baik untuk meningkatkannya. Diputuskan bahwa dalam banyak situasi mengembalikan elemen pertama mengarah pada pengembalian elemen yang tidak diinginkan oleh pengembang untuk dikembalikan.

Jawaban yang paling disukai di sini merekomendasikan untuk digunakan firstatau allalih-alih findtetapi:

  1. alldan firstjangan menunggu sampai elemen dengan pelacak seperti itu akan muncul di halaman meskipun findmenunggu
  2. all(...).firstdan firsttidak akan melindungi Anda dari situasi yang di masa mendatang elemen lain dengan pelacak seperti itu dapat muncul di halaman dan akibatnya Anda mungkin menemukan elemen yang salah

Jadi disarankan untuk memilih pencari lokasi lain yang tidak terlalu ambigu : misalnya pilih elemen berdasarkan id, kelas atau pencari lokasi css / xpath lain sehingga hanya satu elemen yang akan cocok dengannya.


Sebagai catatan, berikut adalah beberapa pencari lokasi yang biasanya saya anggap berguna saat menyelesaikan ambiguitas:

  • find('ul > li:first-child')

    Ini lebih berguna daripada first('ul > li')menunggu sampai pertama kali limuncul di halaman.

  • click_link('Create Account', match: :first)

    Ini lebih baik daripada first(:link, 'Create Account').clickmenunggu sampai setidaknya satu link Buat Akun akan muncul di halaman. Namun saya yakin lebih baik memilih pelacak unik yang tidak muncul di halaman dua kali.

  • fill_in('Password', with: 'secret', exact: true)

    exact: true memberi tahu Kapibara untuk hanya menemukan yang sama persis, yaitu tidak menemukan "Konfirmasi Kata Sandi"

Andrei Botalov
sumber
7
Ini harus menjadi jawaban teratas. Selalu coba gunakan selektor yang akan memanfaatkan kapabilitas menunggu bawaan di Capybara.
tgf
Terima kasih. Saya mencoba menggunakan: pertama tetapi menyadari bahwa hanya berfungsi di jQuery. Apa yang saya cari adalah: anak pertama
Overload119
24

JAWABAN BARU:

Anda bisa mencoba sesuatu seperti

all('a').select {|elt| elt.text == "#tag1" }.first.click

Mungkin ada cara untuk melakukan ini yang memanfaatkan sintaks Capybara yang tersedia dengan lebih baik - sesuatu di sepanjang garis all("a[text='#tag1']").first.clicktetapi saya tidak dapat memikirkan sintaks yang benar begitu saja dan saya tidak dapat menemukan dokumentasi yang sesuai. Yang mengatakan itu sedikit situasi yang aneh untuk memulai dengan, memiliki dua <a>tag dengan yang sama id, class, dan teks. Adakah kemungkinan mereka adalah turunan dari div yang berbeda, karena Anda kemudian dapat melakukan find withinsegmen DOM yang sesuai. (Ini akan membantu untuk melihat sedikit dari sumber HTML Anda).


JAWABAN LAMA: (saat saya pikir '# tag1' berarti elemen tersebut memiliki id"tag1")

Manakah dari link yang ingin Anda klik? Jika itu yang pertama (atau tidak masalah), Anda bisa melakukannya

find('#tag1').click

Jika tidak, Anda bisa melakukannya

all('#tag1')[1].click

untuk mengklik yang kedua.

Amit Kumar Gupta
sumber
Solusi yang pertama mungkin berhasil tetapi masalahnya sekarang adalah mungkin salah untuk id css --------- Failure / Error: find ('# tag1'). Click # or all ('# tag1 ') [0] .click Capybara :: ElementNotFound: Tidak dapat menemukan css "# tag1"
neilmarion
find('#tag1')berarti Anda hanya ingin menemukan satu elemen dengan id tag1. Pengecualian dimunculkan karena ada beberapa elemen dengan id tag1di halaman
Andrei Botalov
Anda bisa melakukannya all(:xpath, '//a[text()="#tag1"]').first.click.
Shuhei Kagawa
9

Anda dapat memastikan bahwa Anda menemukan yang pertama menggunakan match:

find('.selector', match: :first).click

Namun yang terpenting, Anda mungkin tidak ingin melakukan ini , karena ini akan mengarah pada pengujian rapuh yang mengabaikan bau kode keluaran duplikat, yang pada gilirannya mengarah ke positif palsu yang tetap berfungsi ketika seharusnya gagal, karena Anda menghapus satu pencocokan elemen tetapi tes dengan senang hati menemukan yang lain.

Taruhan yang lebih baik adalah menggunakan within:

within('#sidebar') do
  find('.selector).click
end

Ini memastikan bahwa Anda menemukan elemen yang Anda harapkan untuk ditemukan, sambil tetap memanfaatkan kemampuan tunggu otomatis dan coba ulang otomatis dari Capybara (yang hilang jika Anda menggunakannya find('.selector').click), dan itu membuatnya lebih jelas apa maksudnya.

TALlama
sumber
7

Untuk menambah badan pengetahuan yang ada di sini:

Untuk pengujian JS, Capybara harus menjaga dua utas (satu untuk RSpec, satu untuk Rails) dan proses kedua (browser) selaras. Ini dilakukan dengan menunggu (hingga waktu tunggu maksimum yang dikonfigurasi) di kebanyakan matcher dan metode pencarian node.

Kapibara juga memiliki metode yang tidak perlu menunggu Node#all. Menggunakannya seperti memberi tahu spesifikasi Anda bahwa Anda ingin mereka sesekali gagal.

Jawaban yang diterima menyarankan page.first('selector'). Ini tidak diinginkan, setidaknya untuk spesifikasi JS, karena Node#firstmenggunakanNode#all .

Yang mengatakan, Node#first akan menunggu jika Anda mengkonfigurasi kapibara seperti ini:

# rails_helper.rb
Capybara.wait_on_first_by_default = true

Opsi ini telah ditambahkan di Capybara 2.5.0 dan salah secara default.

Seperti yang disebutkan Andrei, Anda sebaiknya menggunakan

find('selector', match: :first)

atau ubah pemilih Anda. Keduanya akan bekerja dengan baik terlepas dari konfigurasi atau drivernya.

Untuk lebih memperumit masalah, di versi lama Kapibara (atau dengan opsi konfigurasi diaktifkan), #finddengan senang hati akan mengabaikan ambiguitas dan hanya mengembalikan pemilih pencocokan pertama. Ini juga tidak bagus, karena membuat spesifikasi Anda kurang eksplisit, yang menurut saya adalah mengapa tidak ada lagi perilaku default. Saya akan mengabaikan spesifikasinya karena mereka sudah dibahas di atas.

Sumber daya lainnya:

johncip
sumber
5

Karena postingan ini , Anda dapat memperbaikinya melalui opsi "cocok":

Capybara.configure do |config|
  config.match = :prefer_exact
end
Skydan
sumber
2

Saat mempertimbangkan semua opsi di atas, Anda juga dapat mencobanya

find("a", text: text, match: :prefer_exact).click

Jika Anda menggunakan mentimun, Anda juga bisa mengikuti ini

Anda dapat mengirimkan teks sebagai parameter dari langkah-langkah skenario yang dapat menjadi langkah umum untuk digunakan kembali

Sesuatu seperti When a user clicks on "text" link

Dan dalam definisi langkah When(/^(?:user) clicks on "([^"]*)" (?:link)$/) do |text|

Dengan cara ini, Anda dapat menggunakan kembali langkah yang sama dengan meminimalkan baris kode dan akan mudah untuk menulis skenario ketimun baru

Kiran Reddy
sumber
0

Untuk menghindari kesalahan ambigu pada mentimun.

Solusi 1

first("#tag1").click

Solusi 2

Cucumber features/filename.feature --guess
Aravin
sumber