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 ...
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