Một vài mẹo để viết test hiệu quả hơn với Capybara

Giới thiệu

Trong bài viết này, tôi sẽ giới thiệu cho các bạn một vài mẹo để viết test hiệu quả hơn với Capybara, và các sử dụng chúng với RSpec và Minitest.

Capybara

Capybara là một framework kiểm thử cho các ứng dụng web. Nó là sự lựa chọn phổ biến cho kiểm thử đầu cuối, chấp nhận và kiểm thử tích hợp trong ứng dụng Rails. Nó cho phép bạn test các ứng dụng web của mình bằng cách mô phỏng một người dùng thực sẽ tương tác với ứng dụng của bạn như thế nào. Mặc định, nó sẽ chạy trong chế độ headless sử dụng Rack::Test, nhưng bạn cũng có thể sử dụng một số drivers khác, như PhantomJS để test trang web của bạn với JavaScript.

Kiểm thử tích hợp và chấp nhận

2 thuật ngữ này là khá rộng trong cộng đồng Rails. Phần lớn Capybara được sử dụng trong kiểm thử tích hợp, ví dụ như feature specs trong RSpec, để test tất cả luồng công việc trong ứng dụng web của bạn từ quan điểm người dùng. Mục đích chính của nó là test tất cả các thành phần của ứng dụng làm việc cùng nhau như thế nào.

RSpec và Minitest

RSpecMinitest là hai test framework thường được sử dụng cho các ứng dụng Rails. Capybara làm việc với cả 2, nhưng cú pháp có thể hơi khác một chút. Bài viết này sẽ không đi sâu vào chi tiết của RSpec và Minitest, nếu bạn đọc muốn tìm hiểu thêm về chúng thì có thể tham khảo tại đây Bắt đầu với RSpecBắt đầu với Minitest.

Một vài mẹo cho việc viết test hiệu quả với Capybara

1. Tìm kiếm linh hoạt hơn với Scoping

Nếu như bạn có một trang lớn, giới hạn vùng cho việc tìm kiếm các thành phần là việc nên làm. Nó giúp giải quyết những vấn đề không mong muốn với những thành phần bị lặp trong trang. Ví dụ, nếu bạn đang làm việc với một thành phần có một nút Sign up, trong khi đã có một nút Sign up khác ở phía trên của trang đó rồi, điều này đòi hỏi bạn cần phải giới hạn vùng tìm kiếm trong trang đó, nếu không muốn test đó xảy ra lỗi.

<div class="header">
  <a href="/signup" class="btn btn-signup">Sign up</a>
</div>

...

<div class="article">
  <a href="/signup" class="btn btn-signup">Sign up</a>
</div>

Một cách để giải quyết vấn đề này là sử dụng within để giới hạn vùng tìm kiếm:

within('.article') do
  click 'Sign up'
end

Phương thức within hỗ trợ sử dụng XPath thay vì tên phần tử, IDs, hay classes:

within(:xpath, '//div[@class="article"]') do
  click 'Sign up'
end

Nếu bạn chỉ thực hiện một hành động bên trong block sử dụng within, thì có một cách khác tương tự sử dụng find:

find('div#article').click 'Sign up'

2. Test các trang chạy JavaScript

Có 2 cách để thực thi JavaScript sử dụng Capybara. Cả 2 đều yêu cầu sử dụng một driver có thể chạy JavaScript. Một cách dễ dàng để thực hiện việc này là sử dụng PhantomJS thông qua gem Poltergeist.

Để cài đặt gem này, chúng ta thêm nó vào Gemfile và chạy bundle:

gem 'poltergeist'

Sau đó, load nó vào trong file setup test của bạn (rails_helper.rb hoặc spec_helper.rb).

require 'capybara/poltergeist'
Capybara.javascript_driver = :poltergeist

Khi bạn đã cầu hình xong JavaScript driver của Capybara, bạn đã có thể thực thi JavaScript trong test của bạn. Trong ví dụ dưới đây, tôi sử dụng jQuery để thay đổi text trên nút Sign up thành Register:

page.execute_script("$('a.btn-signup').text('Register');")

Chúng ta cũng có thể lưu trữ giá trị của một thành phần HTML vào một biến trong Ruby sử dụng JavaScript:

button_text = page.evaluate_script("$('a.btn-signup').text();")

Với cả RSpec và Minitest, JS không được bật lên ở chế độ mặc định, và chúng ta phải nói cho chúng biết để sử dụng Capybara với JavaScript driver cho những test cụ thể cần sử dụng JS.

Trong RSpec, chúng ta thêm js: true như là một đối số thứ 2 cho describe, scenario, contextfeature.

scenario 'Doing something on a page with JavaScript', js: true do
  # Anything you do in there will run JavaScript on pages.
end

Tương tự đối với các test không cần JS:

scenario 'Doing something on a page with JavaScript' do
  # This test will not run any JavaScript on pages or otherwise.
end

Trong Minitest, chúng ta phải nói với Capybara để chuyển đổi drivers, sau đó chuyển về.

def test_with_javascript
  Capybara.current_driver = :poltergeist
  # Anything you do in there will run JavaScript on pages.
  Capybara.use_default_driver
end

Nếu bạn luôn cần JavaScript trong test của bạn, bạn có thể chỉ định `Poltergeist làm driver mặc định trong Capybara:

Capybara.default_driver = :poltergeist

3. Chờ đợi các thành phần JavaScript

Bây giờ, bạn đã biết tất cả về việc chạy JavaScript trong Capybara rồi đúng không ^^. Bạn sẽ gặp phải một vấn đề khi bạn thực thi một vài JavaScript trên trang của bạn, nhưng các assertions của bạn sẽ fail bởi vì Capybara chạy chúng trước khi trang được update.

Rất may mắn, Capybara đã cung cấp cho chúng ta công cụ để vượt qua điều đó. Phương thức find của Capybara sẽ chờ đợi một thành phần xuất hiện trong một khoảng thời gian nhất định. Nó cũng chấp nhận kỳ vọng số lượng để cho Capybara biết phải chờ đợi các kỳ vọng khi sử dụng all. Có 4 kì vọng số lượng: count, maximum, minimumbetween.

all 'a.btn-signup', count: 1
all 'a.btn-signup', minimum: 1
all 'a.btn-signup', maximum: 3
all 'a.btn-signup', between: 1..3

Thời gian mặc định để Capybara chờ đợi các kì vọng có thể được thiết lập ở trong file cài đặt của bạn. Nó được đo bằng đơn vị giây và giá trị mặc định là 2.

Capybara.default_max_wait_time = 5 # Seconds

Thời gian chờ này cũng có thể được thay đổi trong từng phương thức tìm kiếm riêng biệt. Nó sẽ rất hữu ích nếu bạn có một thành phần JavaScript chậm trong test:

all 'a.btn-signup', count: 1, wait: 5

4. Lưu trang web cho việc debug

Capybara có 2 cách khác nhau giúp đỡ việc debug. Mặc dù, một công cụ như pry là hữu dụng nếu bạn muốn thấy trạng thái của các biến, nhưng thỉnh thoảng bạn muốn biết những gì xảy ra với các trang HTML trên trang web của bạn.

Đối với những tình huống đó, chúng ta có thể sử dụng save_and_open_page hoặc save_and_open_screenshot. Chúng được gọi giống nhau trong cả RSpec và Minitest:

scenario 'debugging a failing test' do
  visit root_path
  save_and_open_page
  # Your codes
end

Thao tác này sẽ lưu HTML của trang web trong một tệp tạm thời và mở nó trong trình duyệt của bạn. Các styles sẽ bị thiếu, nhưng nó cũng đã đủ để bạn thấy trạng thái trang web của bạn thế nào và những gì đang xảy ra với nó để phát hiện ra vấn đề. save_and_open_screenshot làm việc giống như vậy, ngoại trừ việc nó sẽ mở một file ảnh trong trang web của bạn.

5. Sử dụng chiến lược Matching các phần tử khác nhau

Mặc định, Capybara sẽ match cả các phần của các thành phần (gọi là partial match). Ví dụ như, cả 2 hành động click sau đây đều match đến nút Sign up:

click 'Sign'
click 'Sign up'

Chúng ta có thể thay đổi hành vi này bằng cách thêm một đối số exact: true. Phương thức click đầu tiên sẽ không match đến nút Sign up nữa:

click 'Sign', exact: true

Chúng ta cũng có thể thay đổi hành vi mặc định này trong file cài đặt:

Capybara.exact = true

Chúng ta cũng có thể cài đặt cách Capybara xử lý việc matching. Có 4 phương thức được xây dựng:

  • :first Nó làm cho phương thức find sẽ luôn trả về phần tử đầu tiên match được và bỏ qua những phần tử còn lại
    Capybara.match = :first
    
  • :one Nó làm cho Capybara raise lên một lỗi khi có nhiều hơn 1 phần tử được match. Sử dụng cài đặt này sẽ ngăn chặn được việc nhiều test bị phá vỡ nằm ngoài hiểu biết của bạn, bởi vì có nhiều hơn một phần tử giống nhau trong một trang.
    Capybara.match = :one
    
  • :prefer_exact Capybara sẽ trả về chính xác một phần tử match nếu nó tồn tại, còn không thì nó sẽ trả về một phần tử không chính xác.
    Capybara.match = :prefer_exact
    
  • :smart Nó là mặc định. Nó sẽ luôn raise một lỗi nếu có nhiều hơn 1 phần tử match, tương tự :one. Tuy nhiên, hành vi này sẽ thay đổi phụ thuộc vào nếu Capybara.exactfalse. Khi exact là false, nó sẽ tìm kiếm một phần tử chính xác, còn nếu không thấy, nó sẽ tìm một partial match.
    Capybara.match = :smart
    



Trên đây, tôi đã giới thiệu một số mẹo để viết test hiệu quả hơn với Capybara. Hi vọng bạn đọc sẽ viết những test tốt, hiệu quả hơn cho trang web của mình nhé.

Nguồn tham khảo

  1. https://semaphoreci.com/community/tutorials/5-tips-for-more-effective-capybara-tests
  2. https://github.com/teamcapybara/capybara