Kapibara Belirsizlik Çözünürlüğü


97

Capybara'daki belirsizliği nasıl çözerim? Bazı nedenlerden dolayı bir sayfada aynı değerlere sahip bağlantılara ihtiyacım var ancak hatayı aldığım için test oluşturamıyorum

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

Bundan kaçamamamın nedeni tasarımdan kaynaklanıyor. Twitter sayfasını sağda tweetler / etiketler ve sayfanın solundaki etiketlerle yeniden oluşturmaya çalışıyorum. Bu nedenle, aynı sayfada aynı bağlantı sayfasının görünmesi kaçınılmaz olacaktır.


Lütfen biraz da kod gönderebilir misiniz?
Heena Hüseyin

8
Sayfadaki iki öğeye aynı kimliği atamamalısınız. Aynı bağlantılara sahip olacaksanız, öğelere bir kimlik atamayın, bunun yerine bir sınıf kullanın.
Chris Salzberg

Yanıtlar:


147

Benim çözümüm

first(:link, link).click

onun yerine

click_link(link)

6
Bu, Capybara Yükseltme Kılavuzunda ayrıntılı olarak bu sorunu yaşadıysanız yararlı bulabilir.
Ritchie

1
Capybara 2.0'dan itibaren kesinlikle mecbur kalmadıkça bunu yapmayın. Aşağıdaki @ Andrey'in cevabına ve yukarıda bağlantısı verilen yükseltme kılavuzundaki Belirsiz Eşleşmelerin açıklamasına bakın.
jim

4
Spesifik olarak, Capybara 2.0, teknik özelliklerin yalnızca gerekli minimum süreyi beklerken farklı işleme hızlarına sahip makineler arasında tutarlı bir şekilde geçmesini veya başarısız olmasını sağlamak için akıllı bekleme mantığına sahiptir. firstYukarıda önerildiği gibi kullanmak , ne yaptığınızı kesinlikle bilmiyorsanız, muhtemelen sizin için geçerli olan ancak bir CI yapısında veya bir iş arkadaşınızın makinesinde başarısız olan özelliklerle sonuçlanır.
jim

1
İyi bir tartışma için bakınız: robots.thoughtbot.com/…
jim

74

Capybara'nın bu tür davranışları kasıtlıdır ve diğer yanıtların çoğunda önerildiği gibi düzeltilmemesi gerektiğine inanıyorum.

Capybara'nın 2.0'dan önceki sürümleri, istisnayı yükseltmek yerine ilk öğeyi döndürdü, ancak daha sonra Capybara'nın geliştiricileri bunun kötü bir fikir ve onu yükseltmenin daha iyi olduğuna karar verdi. Birçok durumda, ilk elementin geri döndürülmesinin, geliştiricinin iade edilmesini istediği elementin değil, geri dönüşe yol açtığına karar verildi.

Buradaki en çok oy alan cevap , ancak yerine firstveya allyerine kullanmanızı önerir find:

  1. allve firstböyle bir bulucuya sahip öğenin sayfada görünmesini findbeklemeyin , ancak bekleyin
  2. all(...).firstve firstsizi gelecekte bu tür bir konum belirleyiciye sahip başka bir öğenin sayfada görünebileceği ve bunun sonucunda yanlış öğe bulabileceğiniz durumdan korumaz

Bu nedenle , daha az belirsiz başka bir konum belirleyici seçmeniz önerilir : örneğin, öğeyi id, sınıf veya diğer css / xpath konumlandırıcıya göre seçin, böylece yalnızca bir öğe eşleşir.


Burada bir not olarak, belirsizliği çözerken genellikle yararlı olduğunu düşündüğüm bazı konumlandırıcılar:

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

    Sayfada first('ul > li')ilk ligörünene kadar bekleyeceğinden daha kullanışlıdır .

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

    first(:link, 'Create Account').clickSayfada en az bir Hesap Oluştur bağlantısı görünene kadar beklemekten daha iyidir . Ancak, sayfada iki kez görünmeyen benzersiz konum belirleyiciyi seçmenin daha iyi olduğuna inanıyorum.

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

    exact: true Capybara'ya sadece tam eşleşmeleri bulmasını, yani "Şifre Onayını" bulmamasını söyler


7
Bu en iyi cevap olmalı. Daima Capybara'daki yerleşik bekleme yeteneklerinden yararlanacak bir seçici kullanmaya çalışın.
tgf

Teşekkürler. Kullanmaya çalıştım: ilk önce ancak bunun yalnızca jQuery'de çalıştığını fark ettim. Aradığım şey: first-child
Overload119


24

YENİ CEVAP:

Gibi bir şey deneyebilirsin

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

Bunu yapmanın mevcut Capybara sözdizimini daha iyi kullanan bir yolu olabilir - bu satırlar boyunca bir şey all("a[text='#tag1']").first.clickama elimden doğru sözdizimini düşünemiyorum ve uygun belgeleri bulamıyorum. İşte bu tuhaf durumun biraz iki sahip başlamak olduğunu söyledi <a>aynı etiketleri id, classve metin. DOM'un find withinuygun segmentini yapabileceğiniz için , farklı div'lerin çocukları olma ihtimalleri var mı ? (HTML kaynağınızın bir kısmını görmek yardımcı olacaktır).


ESKİ CEVAP: ('# etiket1'in öğenin id"etiket1" olduğu anlamına geldiğini düşündüm )

Bağlantılardan hangisine tıklamak istiyorsunuz? Eğer ilkse (veya önemli değilse), yapabilirsin

find('#tag1').click

Aksi takdirde yapabilirsin

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

ikincisini tıklamak için.


İlk çözümdeki bu çözüm işe yarayabilir, ancak şimdi sorun şu ki, bir css kimliği ile karıştırılmış olabilir --------- Hata / Hata: bul ('# tag1'). # Veya tümünü tıklayın ('# tag1 ') [0] .click Capybara :: ElementNotFound: css "# tag1"
bulunamıyor

find('#tag1')kimliğine sahip yalnızca bir öğe bulmak istediğiniz anlamına gelir tag1. tag1Sayfada kimliği olan birkaç öğe olduğu için istisna gündeme geldi
Andrei Botalov

Yapabilirsin all(:xpath, '//a[text()="#tag1"]').first.click.
Shuhei Kagawa

9

Şunları kullanarak ilkini bulduğunuzdan emin olabilirsiniz match:

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

Ancak daha da önemlisi, muhtemelen bunu yapmak istemezsiniz çünkü bu , yinelenen çıktı kodu kokusunu görmezden gelen kırılgan testlere yol açar ve bu da, başarısız olmaları gerektiğinde çalışmaya devam eden yanlış pozitiflere yol açar , çünkü bir eşleşmeyi kaldırdınız ama test mutlu bir şekilde diğerini buldu.

Daha iyi bahis withinşunları kullanmaktır :

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

Bu, bulmayı beklediğiniz öğeyi bulmanızı sağlarken, Capybara'nın (kullanırsanız kaybedeceğiniz find('.selector').click) otomatik bekleme ve otomatik yeniden deneme yeteneklerinden yararlanmaya devam etmenizi sağlar ve amacın ne olduğunu çok daha net hale getirir.


7

Buradaki mevcut bilgi birikimine eklemek için:

JS testleri için, Capybara'nın iki iş parçacığı (biri RSpec için, biri Rails için) ve ikinci bir işlemi (tarayıcı) senkronize tutması gerekir. Bunu çoğu eşleştiricide ve düğüm bulma yönteminde (yapılandırılmış maksimum bekleme süresine kadar) bekleyerek yapar.

Kapibara da öncelikle beklemeyen yöntemlere sahiptir Node#all. Bunları kullanmak, teknik özelliklerinize aralıklı olarak başarısız olmalarını istediğinizi söylemek gibidir.

Kabul edilen cevap öneriyor page.first('selector'). Bu, en azından JS özellikleri için istenmeyen bir durumdur, çünkü Node#firstkullanırNode#all .

Yani, söz konusu Node#first olacak kadar böyle Capybara yapılandırmak eğer bekleyin:

# rails_helper.rb
Capybara.wait_on_first_by_default = true

Bu seçenek Capybara 2.5.0'da eklenmiştir ve varsayılan olarak yanlıştır.

Andrei'nin bahsettiği gibi, bunun yerine kullanmalısınız

find('selector', match: :first)

veya seçicinizi değiştirin. Her ikisi de yapılandırma veya sürücüden bağımsız olarak iyi çalışacaktır.

İşleri daha da karmaşık hale getirmek için, Capybara'nın eski sürümlerinde (veya bir yapılandırma seçeneği etkinken), #findbelirsizliği memnuniyetle yok sayacak ve sadece ilk eşleşen seçiciyi döndürecektir. Spesifikasyonlarınızı daha az açık hale getirdiği için bu da harika değil, sanırım neden artık varsayılan davranış değil. Ayrıntıları dışarıda bırakacağım çünkü yukarıda zaten tartışılmışlardı.

Daha fazla kaynak:


5

Bu gönderi nedeniyle , bunu "eşleştirme" seçeneğiyle düzeltebilirsiniz:

Capybara.configure do |config|
  config.match = :prefer_exact
end

2

Yukarıdaki tüm seçenekleri göz önünde bulundurarak, bunu da deneyebilirsiniz.

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

Salatalık kullanıyorsanız bunu da takip edebilirsiniz.

Metni, tekrar kullanmak için genel bir adım olabilecek senaryo adımlarından bir parametre olarak iletebilirsiniz.

Gibi bir şey When a user clicks on "text" link

Ve adım tanımında When(/^(?:user) clicks on "([^"]*)" (?:link)$/) do |text|

Bu şekilde, kod satırlarını en aza indirerek aynı adımı yeniden kullanabilirsiniz ve yeni salatalık senaryoları yazmak kolay olacaktır.


0

Salatalıkta belirsiz hatalardan kaçınmak için.

1.Çözüm

first("#tag1").click

2.Çözüm

Cucumber features/filename.feature --guess
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.