Chain RSpec Matchers for Improved Test Readability

Một trong những đặc điểm mà nhiều người thích ở Ruby đó chính là khả năng Readability của Ruby code. Vì vậy, một người code Ruby hay là người có khả năng làm code cho họ dễ đọc nhất có thể.

Original Code

select_multiple_from "Which of these apply to you?", [
  "I read the New York Times every day",
  "I read the Washington Post every day",
]

click_on "Submit"

expect(page).to have_css("dd ul li", count: 2)
expect(page).to have_css("dd ul li", text: "I read the New York Times every day")
expect(page).to have_css("dd ul li", text: "I read the Washington Post every day")

nhiệm vụ của method select_multiple_from là handle multi-select

def select_multiple_from from, options
  options.each do |option|
    select option, from: from
  end
end

Refactor

3 dòng assertion ở trên có vẻ là không có ý nghĩa cho lắm, nhưng nó là cần thiết:

  • Đảm bảo số lượng item được chọn trong multi-select là chính xác, không thừa, không thiếu

Khi viết Ruby code, đặc biệt là Ruby code focus vào acceptance test, tôi thường xuyên focus vào tính Readability của nó.

expect(page).to have_multiple_choice_responses(
  "I read the New York Times every day",
  "I read the Washington Post every day"
)

Với đoạn code trên chúng ta sẽ cover rằng chỉ có 2 và chỉ 2 items xuất hiện trong list. Nhưng trong trường hợp chúng ta không chỉ cover số lượng item xuất hiện, chúng ta có thể sử dụng featureand của Rspec. Khi sử dụng với have_css của Capybara chúng ta có thể refactor lại code trên như sau:

def have_multiple_choice_responses(item1, item2)
  have_css("dd ul li", count: 2).
    and(have_css("dd ul li", text: item1)).
    and(have_css("dd ul li", text: item2))
end

Nhưng 1 lần nữa, đoạn code trên lại gặp vấn đến với số lượng item. Chúng ta sẽ cover trường hợp đó với: Enumrable inject

def have_multiple_choice_responses(*args)
  count_matcher = have_css("dd ul li", count: args.length)

  args.inject(count_matcher) do |matcher, text|
    matcher.and(have_css("dd ul li", text: text))
  end
end

Đoạn code trên cover được trường hợp list có chứa đầy đủ các item, trường hợp chúng ta muốn cover thêm trường hợp thứ tự của các item đó trong list:

def have_multiple_choice_responses(*args)
  count_matcher = have_css("dd ul li", count: args.length)

  args.each.with_index(1).inject(count_matcher) do |matcher, (text, offset)|
    matcher.and(have_css("dd ul li:nth-of-type(#{offset})", text: text))
  end
end

Do là li:nth-of-type có offset là 1 nên ta phải sử dụng with_index(1)

Conclusion

Với đoạn code đã refactor ở trên, chúng ta đã sửa dụng Rspec với Ruby để tạo ra các assertion dễ đọc, dễ maintenance. Thanks for reading. Happy coding!

Refs

https://robots.thoughtbot.com/chain-rspec-matchers-for-improved-test-readability

All Rights Reserved