DÙNG LET HAY KHÔNG?
Ruby
119
Testing
30
White

Hieu Nguyen viết ngày 27/12/2015

DÙNG LET HAY KHÔNG?

Mở đầu

Khi chúng ta (lập trình viên Ruby on Rails) viết test, cho dù sử dụng rspec hay minitest, đều sẽ dùng let rất nhiều. Cú pháp let giúp chúng ta viết code dễ dàng và tiện lợi hơn rất nhiều:

def activable?
  inactive? && !blacklist?
end

describe '#activable?' do
  let(:inactive?) { true }
  let(:blacklist?) { false }
  subject { build_stubbed(:user) }

  before do
    expect(subject).to receive(:inactive?).and_return(inactive?)
    expect(subject).to receive(:blacklist?).and_return(blacklist?)
  end

  context 'when user is active' do
    let(:inactive?) { false }
    it { is_expected.not_to be_activable }
  end

  context 'when user is blacklist' do
    let(:blacklist?) { true }
    it { is_expected.not_to be_activable }
  end

  context 'otherwise' do
    it { is_expected.to be_activable }
  end
end

Như các bạn thấy ở trên, ở mỗi context chúng ta override một giá trị duy nhất tương ứng với trường hợp này. Điều này giúp ta hai điều:

  • Tránh lặp lại code, giúp code ngắn và dễ đọc hơn
  • Thể hiện rõ từng điều kiện của từng context

Tuy nhiên, gần đây, trong một cuộc thảo luận với bạn của mình, mình được biết một số công ty như Thoughtbot và Thoughtworks đã khuyến cáo không sử dụng cách làm này.

Vấn đề của let

Các kỹ sư của Thoughtbot có một bài nói về lý do không sử dụng let, qua đó đề cập đến hai vấn đề:

  • Nhập nhằng trong bước setup dữ liệu. Do let là lazyload, đôi khi bạn sẽ phải gặp phải lỗi là thời điểm object được khởi tạo sau khi test được thực hiện:
describe '.active' do
  let(:user) { create(:user, active: active) }
  subject { User.active }

  describe 'when user is active' do
    let(:active) { true }    
    it { is_expected.to eq [user] } # this test will fail
  end

  # ...
end
  • Dễ gặp lỗi và tight coupling giữa các dữ liệu được setup, đặc biệt là khi test phức tạp.

Hãy cùng xem một ví dụ khác ở đoạn code sau.

Ở ví dụ trên, trong phần sẽ rất khó để người đọc biết được vị trí của lỗi sai là nằm ở đâu do biến attributes đã bị override quá nhiều lần. Họ cũng không thể phân biệt được đâu là phần setup dữ liệu, đâu là phần test.

Giải pháp được đưa ra

Các kỹ sư của Thoughtbot đưa ra một số giải pháp cho tình trạng trên:

  • Chấp nhận code duplicate ở một số trường hợp
  • Tuân thủ theo nguyên tắc 4 bước trong testing: test phải phân tách rõ ràng 4 bước setup, execute, assertion và tear down.
  • Không sử dụng let, subject hoặc các method tương tự.

Ta sẽ refactor lại testcase ở bên trên theo các tiêu chí này, ta có đoạn code ở link sau.

Ta có thể thấy là đoạn test mới này dễ đọc hơn rất nhiều so với ví dụ ở phần 2. Chúng ta biết rõ ràng dữ liệu được setup như thế này, do đó sẽ dễ dàng debug hoặc chỉnh sửa các test này khi cần thiết.

Tuy nhiên, không phải là không có vấn đề khi sử dụng phương pháp này. Khi test của bạn ngày càng dài, bạn sẽ càng phải viết nhiều dupplicate code, do đó test của bạn sẽ ngày càng khó đọc, khó debug. Để tránh tình trạng này, bạn sẽ dần phải refactor code để giảm duplication và chia tách các concern. Dần dần, với quá nhiều redirection, code của bạn cũng sẽ trở lại tình trang khi sử dụng let. Mặc dù vậy, do đây chỉ là ruby code và bạn nắm toàn quyền quản lý code, bạn có thể tạo ra những abstraction dễ đọc và hiểu hơn thông qua OOP hoặc FP.

Kết luận

Mình vẫn chưa cảm thấy việc loại bỏ hoàn toàn let khỏi test là hợp lý. Mỗi phương pháp đều có lợi điểm riêng và phù hợp với từng trường hợp nhất định. Dù vậy, việc consistent khi viết code (cũng như viết test) là điều cần thiết mà bất cứ project nào cũng cần có, và mình vẫn còn lấn cấn ở điểm này. Do vậy, hi vọng qua bài viết này các bạn có thể cho mình thêm ý kiến về vấn đề này.

Bình luận


White
{{ comment.user.name }}
Bỏ hay Hay
{{comment.like_count}}
Male avatar
{{ comment_error }}
Hủy
   

Hiển thị thử

Chỉnh sửa

White

Hieu Nguyen

22 bài viết.
10 người follow
Kipalog
{{userFollowed ? 'Following' : 'Follow'}}
Cùng một tác giả
White
12 3
Khi làm việc với Ruby và Rails, có lẽ không ít lần các bạn đã gặp các tác vụ download file về server của mình. Ruby hỗ trợ nhiều công cụ download k...
Hieu Nguyen viết hơn 5 năm trước
12 3
White
12 4
Đối với những người đã từng xây dựng API server, chắc hẳn ai cũng đã từng hơn một lần đau đầu với việc lựa chọn thư viện JSON. Bài viết sau đây hi ...
Hieu Nguyen viết hơn 5 năm trước
12 4
White
11 0
Như đã nói trong (Link), mình sẽ chia sẻ tổng hợp kết quả của các benchmark mình đã dùng để so sánh tốc độ render JSON của các thư viện phổ biến hi...
Hieu Nguyen viết hơn 5 năm trước
11 0
Bài viết liên quan
White
6 0
1. Định nghĩa Một kế hoạch kiểm thử dự án phần mềm (test plan) là một tài liệu mô tả các mục tiêu, phạm vi, phương pháp tiếp cận, và tập trung vào...
Thiên Hoàng Minh Vũ viết hơn 2 năm trước
6 0
White
9 6
Chưa xem phần 2? Xem (Link) Trong bài viết này tôi giới thiệu cho các bạn về khái niệm function arity, một cách gọi mĩ miều của số lượng argument ...
Lơi Rệ viết 5 năm trước
9 6
{{like_count}}

kipalog

{{ comment_count }}

bình luận

{{liked ? "Đã kipalog" : "Kipalog"}}


White
{{userFollowed ? 'Following' : 'Follow'}}
22 bài viết.
10 người follow

 Đầu mục bài viết

Vẫn còn nữa! x

Kipalog vẫn còn rất nhiều bài viết hay và chủ đề thú vị chờ bạn khám phá!