Capybara Độ phân giải mơ hồ


97

Làm cách nào để giải quyết sự mơ hồ trong Capybara? Vì lý do nào đó, tôi cần các liên kết có cùng giá trị trong một trang nhưng tôi không thể tạo kiểm tra vì tôi gặp lỗi

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

Lý do tại sao tôi không thể tránh điều này là vì thiết kế. Tôi đang cố gắng tạo lại trang twitter với các tweet / thẻ ở bên phải và các thẻ ở bên trái của trang. Do đó, sẽ không thể tránh khỏi các trang liên kết giống hệt nhau hiển thị trên cùng một trang.


Bạn có thể vui lòng gửi một số mã cũng được?
Heena Hussain

8
Bạn không nên gán cùng một id cho hai phần tử trên trang. Nếu bạn có các liên kết giống hệt nhau, thì đừng gán id cho các phần tử, thay vào đó hãy sử dụng một lớp.
Chris Salzberg

Câu trả lời:


147

Giải pháp của tôi là

first(:link, link).click

thay vì

click_link(link)

6
Điều này được trình bày chi tiết trong Hướng dẫn nâng cấp Capybara mà bạn có thể thấy hữu ích nếu gặp sự cố này.
Ritchie

1
Đối với Capybara 2.0, không làm điều này trừ khi bạn hoàn toàn phải làm. Xem câu trả lời của @ Andrey bên dưới và giải thích về Kết quả phù hợp mơ hồ trong hướng dẫn nâng cấp được liên kết ở trên.
jim

4
Cụ thể, Capybara 2.0 có logic chờ thông minh để đảm bảo các thông số kỹ thuật vượt qua hoặc thất bại một cách nhất quán giữa các máy có tốc độ xử lý khác nhau trong khi chỉ chờ thời gian cần thiết tối thiểu. Sử dụng firstnhư được đề xuất ở trên, trừ khi bạn hoàn toàn biết mình đang làm gì, có khả năng dẫn đến thông số kỹ thuật vượt qua cho bạn nhưng không thành công trong bản dựng CI hoặc trên máy của đồng nghiệp.
jim

1
Để có một cuộc thảo luận tốt, hãy xem: robots.thoughtbot.com/…
jim

74

Hành vi như vậy của Capybara là có chủ ý và tôi tin rằng nó không nên được sửa chữa như đã đề xuất trong hầu hết các câu trả lời khác.

Các phiên bản của Capybara trước 2.0 trả lại phần tử đầu tiên thay vì nâng cao ngoại lệ nhưng những người bảo trì Capybara sau đó đã quyết định rằng đó là một ý tưởng tồi và tốt hơn là bạn nên nâng nó lên. Người ta đã quyết định rằng trong nhiều tình huống, việc trả lại phần tử đầu tiên dẫn đến việc trả lại không phải phần tử mà nhà phát triển muốn được trả lại.

Câu trả lời được ủng hộ nhiều nhất ở đây khuyên bạn nên sử dụng firsthoặc allthay vìfind nhưng:

  1. allfirst đừng đợi cho đến khi phần tử có bộ định vị như vậy sẽ xuất hiện trên trang mặc dù findphải đợi
  2. all(...).firstfirst sẽ không bảo vệ bạn khỏi tình huống trong tương lai một phần tử khác với bộ định vị như vậy có thể xuất hiện trên trang và kết quả là bạn có thể tìm thấy phần tử không chính xác

Vì vậy, bạn nên chọn một bộ định vị khác ít mơ hồ hơn : ví dụ: chọn phần tử theo id, lớp hoặc bộ định vị css / xpath khác để chỉ một phần tử phù hợp với nó.


Như một lưu ý ở đây là một số công cụ định vị mà tôi thường coi là hữu ích khi giải quyết sự mơ hồ:

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

    Nó hữu ích hơn first('ul > li')là nó sẽ đợi cho đến khi đầu tiên lixuất hiện trên trang.

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

    Nó còn tốt hơn first(:link, 'Create Account').clickvì nó sẽ đợi cho đến khi ít nhất một liên kết Tạo tài khoản xuất hiện trên trang. Tuy nhiên, tôi tin rằng tốt hơn nên chọn công cụ định vị duy nhất không xuất hiện trên trang hai lần.

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

    exact: true yêu cầu Capybara chỉ tìm các kết quả phù hợp chính xác, tức là không tìm thấy "Xác nhận mật khẩu"


7
Đây phải là câu trả lời hàng đầu. Luôn cố gắng sử dụng một bộ chọn sẽ tận dụng khả năng chờ tích hợp trong Capybara.
tgf

Cảm ơn. Tôi đã cố gắng sử dụng: đầu tiên nhưng nhận ra rằng chỉ hoạt động trong jQuery. Những gì tôi đang tìm kiếm là: đứa con đầu lòng
Quá tải119


24

CÂU TRẢ LỜI MỚI:

Bạn có thể thử một cái gì đó như

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

Có thể có một cách để làm điều này giúp sử dụng tốt hơn cú pháp Capybara có sẵn - một cái gì đó dọc theo dòng all("a[text='#tag1']").first.clicknhưng tôi không thể nghĩ ra cú pháp chính xác và tôi không thể tìm thấy tài liệu thích hợp. Điều đó nói rằng đó là một chút của một tình huống kỳ lạ để bắt đầu với, có hai <a>thẻ với cùng id, classvà văn bản. Có cơ hội nào họ là con của các div khác nhau không, vì sau đó bạn có thể thực hiện find withinphân đoạn DOM thích hợp của mình. (Sẽ hữu ích khi xem một chút nguồn HTML của bạn).


CÂU TRẢ LỜI CŨ: (trong đó tôi nghĩ '# tag1' có nghĩa là phần tử có id"tag1")

Bạn muốn nhấp vào liên kết nào trong số các liên kết? Nếu đó là lần đầu tiên (hoặc không quan trọng), bạn có thể làm

find('#tag1').click

Nếu không bạn có thể làm

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

để nhấp vào cái thứ hai.


Giải pháp đó ở giải pháp đầu tiên có thể hoạt động nhưng vấn đề bây giờ là nó có thể bị nhầm lẫn với id css --------- Failure / Error: find ('# tag1'). Nhấp vào # hoặc tất cả ('# tag1 ') [0] .click Capybara :: ElementNotFound: Không thể tìm thấy css "#
tag1

find('#tag1')nghĩa là bạn chỉ muốn tìm một phần tử có id tag1. Ngoại lệ xảy ra là có một số yếu tố với id tag1trên trang
Andrei Botalov

Bạn có thể làm được all(:xpath, '//a[text()="#tag1"]').first.click.
Shuhei Kagawa

9

Bạn có thể đảm bảo rằng bạn tìm thấy cái đầu tiên bằng cách sử dụng match:

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

Nhưng quan trọng là bạn có thể không muốn làm điều này , vì nó sẽ dẫn đến các thử nghiệm giòn bỏ qua mùi mã đầu ra trùng lặp, do đó dẫn đến kết quả dương tính giả tiếp tục hoạt động khi đáng lẽ không thành công, vì bạn đã xóa một kết quả phù hợp. phần tử nhưng các thử nghiệm vui vẻ tìm thấy một trong những khác.

Đặt cược tốt hơn là sử dụng within:

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

Điều này đảm bảo rằng bạn đang tìm thấy phần tử mà bạn mong đợi, trong khi vẫn tận dụng khả năng tự động chờ và tự động thử lại của Capybara (mà bạn sẽ mất nếu sử dụng find('.selector').click) và nó làm cho nó rõ ràng hơn nhiều so với mục đích.


7

Để thêm vào khối kiến ​​thức hiện có tại đây:

Đối với các bài kiểm tra JS, Capybara phải giữ hai luồng (một cho RSpec, một cho Rails) và một quá trình thứ hai (trình duyệt) đồng bộ. Nó thực hiện điều này bằng cách chờ (lên đến thời gian chờ tối đa đã định cấu hình) trong hầu hết các phương pháp tìm nút và đối sánh.

Capybara chủ yếu cũng có các phương thức không chờ đợi Node#all. Sử dụng chúng giống như nói với thông số kỹ thuật của bạn rằng bạn muốn chúng không liên tục.

Câu trả lời được chấp nhận gợi ý page.first('selector'). Điều này là không mong muốn, ít nhất là đối với các thông số kỹ thuật JS, bởi vì việc Node#firstsử dụngNode#all .

Điều đó nói rằng, Node#first sẽ đợi nếu bạn cấu hình Capybara như vậy:

# rails_helper.rb
Capybara.wait_on_first_by_default = true

Tùy chọn này đã được thêm vào Capybara 2.5.0 và là sai theo mặc định.

Như Andrei đã đề cập, thay vào đó bạn nên sử dụng

find('selector', match: :first)

hoặc thay đổi bộ chọn của bạn. Một trong hai sẽ hoạt động tốt bất kể cấu hình hoặc trình điều khiển.

Để làm phức tạp thêm mọi thứ, trong các phiên bản cũ của Capybara (hoặc với tùy chọn cấu hình được bật), #findsẽ vui vẻ bỏ qua sự mơ hồ và chỉ trả lại bộ chọn phù hợp đầu tiên. Điều này cũng không tuyệt vời vì nó làm cho thông số kỹ thuật của bạn ít rõ ràng hơn, điều mà tôi tưởng tượng là lý do tại sao không còn là hành vi mặc định nữa. Tôi sẽ để lại các chi tiết cụ thể vì chúng đã được thảo luận ở trên.

Nhiêu tai nguyên hơn:


5

Do bài đăng này , bạn có thể sửa nó thông qua tùy chọn "khớp":

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

2

Khi xem xét tất cả các tùy chọn ở trên, bạn cũng có thể thử điều này

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

Nếu bạn đang sử dụng dưa chuột, bạn cũng có thể làm theo

Bạn có thể chuyển văn bản dưới dạng tham số từ các bước kịch bản có thể là bước chung để sử dụng lại

Cái gì đó như When a user clicks on "text" link

Và trong định nghĩa bước When(/^(?:user) clicks on "([^"]*)" (?:link)$/) do |text|

Bằng cách này, bạn có thể sử dụng lại bước tương tự bằng cách giảm thiểu các dòng mã và sẽ dễ dàng viết các kịch bản dưa chuột mới


0

Để tránh lỗi mơ hồ ở dưa chuột.

Giải pháp 1

first("#tag1").click

Giải pháp 2

Cucumber features/filename.feature --guess
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.