Giải quyết các vấn đề đi kèm với hàm assadEquals (dự kiến, thực tế)


10

Sau nhiều năm viết mã cao bồi, tôi quyết định chọn một cuốn sách về cách viết mã chất lượng tốt. Tôi đang đọc Clean Code của Robert Cecil Martin. Trong chương 3 (chức năng) có một phần về chức năng dyadic. Đây là một đoạn trích từ cuốn sách.

Ngay cả các chức năng dyadic rõ ràng như assertEquals(expected, actual)là có vấn đề. Đã bao nhiêu lần bạn đặt thực tế nơi dự kiến ​​sẽ được? Hai đối số không có trật tự tự nhiên. Thứ tự dự kiến, thực tế là một quy ước đòi hỏi phải thực hành để học hỏi.

Tác giả làm cho một điểm hấp dẫn. Tôi làm việc trong máy học và bắt gặp điều này mọi lúc. Ví dụ: tất cả các hàm số liệu trong thư viện sklearn (có thể là thư viện python được sử dụng nhiều nhất trong trường) yêu cầu bạn phải cẩn thận về thứ tự của các đầu vào. Như một ví dụ sklearn.metrics.homogeneity_score lấy làm đầu vào labels_truelabels_pred. Những gì chức năng này không quá liên quan, những gì có liên quan là nếu bạn chuyển đổi thứ tự của các đầu vào, sẽ không có lỗi nào được đưa ra. Trong thực tế, việc chuyển đổi các đầu vào tương đương với việc sử dụng một chức năng khác trong thư viện.

Tuy nhiên, cuốn sách không nói rằng một sửa chữa hợp lý cho các chức năng như assertEquals. Tôi không thể nghĩ ra cách khắc phục cho assertEqualshoặc cho các chức năng mà tôi thường gặp như cách mô tả ở trên. Thực hành tốt để giải quyết vấn đề này là gì?

Câu trả lời:


11

Thật tốt khi nhận thức được một vấn đề có thể xảy ra ngay cả khi không có cách khắc phục - theo cách đó bạn có thể cảnh giác khi đọc hoặc viết mã như vậy. Trong ví dụ cụ thể này, bạn chỉ cần làm quen với thứ tự các đối số sau một thời gian.

Có các cách ở cấp độ ngôn ngữ để ngăn chặn bất kỳ sự nhầm lẫn nào về thứ tự tham số: các đối số được đặt tên. Rất tiếc, điều này không được hỗ trợ trong nhiều ngôn ngữ với cú pháp kiểu C như Java hoặc C ++. Nhưng trong Python, mọi đối số có thể là một đối số có tên. Thay vì gọi một chức năng def foo(a, b)như foo(1, 2), chúng ta có thể làm foo(a=1, b=2). Nhiều ngôn ngữ hiện đại như C # có cú pháp tương tự. Gia đình ngôn ngữ Smalltalk đã đưa ra các đối số được đặt tên xa nhất: không có bất kỳ đối số vị trí nào và mọi thứ đều được đặt tên. Điều này có thể dẫn đến mã đọc rất gần với ngôn ngữ tự nhiên.

Một cách khác thiết thực hơn là tạo các API mô phỏng các đối số được đặt tên. Đây có thể là các API thông thạo hoặc các hàm trợ giúp tạo ra một luồng tự nhiên. Cái assertEquals(actual, expected)tên khó hiểu. Một số lựa chọn thay thế tôi đã thấy:

  • assertThat(actual, is(equalTo(expected))): bằng cách gói một số đối số trong các loại trình trợ giúp, các hàm gói có hiệu quả đóng vai trò là tên tham số. Trong trường hợp cụ thể của các xác nhận kiểm tra đơn vị, kỹ thuật này được sử dụng bởi các trình so khớp Hamcrest để cung cấp một hệ thống xác nhận mở rộng và có thể kết hợp. Nhược điểm ở đây là bạn nhận được rất nhiều lồng nhau, và cần nhập nhiều hàm trợ giúp. Đây là kỹ thuật đi đến của tôi trong C ++.

  • expect(actual).to.be(expected): một API thông thạo nơi bạn kết hợp các hàm gọi với nhau. Trong khi điều này tránh làm tổ thêm, điều này không thể mở rộng. Mặc dù tôi thấy rằng các API thông thạo đọc rất tốt, nhưng việc thiết kế một API thông thạo tốt có xu hướng mất nhiều công sức theo kinh nghiệm của tôi, bởi vì bạn cần triển khai các lớp bổ sung cho các trạng thái không đầu cuối trong chuỗi cuộc gọi. Nỗ lực này chỉ thực sự được đền đáp trong bối cảnh IDE tự động hoàn thành có thể đề xuất các cuộc gọi phương thức được phép tiếp theo.


4

Có một số phương pháp để tránh vấn đề này. Một phương pháp không bắt buộc bạn phải thay đổi phương thức bạn gọi:

Thay vì

assertEquals( 42, meaningOfLife() ); 

Sử dụng

expected = 42;
actual = meaningOfLife();
assertEquals(expected, actual);

Điều này buộc công ước phải mở ra nơi dễ dàng nhận ra họ đang chuyển đổi. Chắc chắn nó không dễ viết như vậy nhưng nó rất dễ đọc.

Nếu bạn có thể thay đổi phương thức được gọi, bạn có thể sử dụng hệ thống gõ để buộc sử dụng dễ đọc.

assertThat( meaningOfLife(), is(42) );

Một số ngôn ngữ cho phép bạn tránh điều này vì chúng có các tham số được đặt tên:

assertEquals( expected=42, actual=meaningOfLife() );

Những người khác không nên bạn mô phỏng chúng:

assertEquals().expected(42).actual( meaningOfLife() );

Bất cứ điều gì bạn làm đều tìm thấy một cách rõ ràng, chính xác khi đọc. Đừng làm tôi đoán hội nghị là gì. Cho tôi xem.

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.