Kiểm thử đơn vị mã chức năng gõ tĩnh


15

Tôi muốn hỏi mọi người, trong trường hợp này có ý nghĩa để kiểm tra đơn vị mã chức năng được nhập tĩnh, như được viết bằng haskell, scala, ocaml, nemerle, f # hoặc haXe (cuối cùng là điều tôi thực sự quan tâm, nhưng tôi muốn khai thác kiến ​​thức của các cộng đồng lớn hơn).

Tôi hỏi điều này bởi vì từ sự hiểu biết của tôi:

  • Một khía cạnh của các bài kiểm tra đơn vị là có các thông số kỹ thuật ở dạng có thể chạy được. Tuy nhiên, khi sử dụng một kiểu khai báo, ánh xạ trực tiếp các thông số kỹ thuật chính thức vào ngữ nghĩa ngôn ngữ, thậm chí có thể thực sự thể hiện các thông số kỹ thuật ở dạng có thể chạy theo một cách riêng biệt, điều đó làm tăng giá trị?

  • Khía cạnh rõ ràng hơn của các bài kiểm tra đơn vị là theo dõi các lỗi không thể tiết lộ thông qua phân tích tĩnh. Cho rằng loại mã chức năng an toàn là một công cụ tốt để mã rất gần với những gì máy phân tích tĩnh của bạn hiểu, có vẻ như bạn có thể chuyển nhiều an toàn sang phân tích tĩnh. Tuy nhiên, một lỗi đơn giản như sử dụng xthay vì y(cả hai đều là tọa độ) trong mã của bạn có thể được bảo vệ. OTOH một lỗi như vậy cũng có thể phát sinh trong khi viết mã kiểm tra, vì vậy tôi không chắc liệu nó có đáng để nỗ lực hay không.

  • Các thử nghiệm đơn vị thực hiện giới thiệu dự phòng, có nghĩa là khi các yêu cầu thay đổi, mã thực hiện chúng và các thử nghiệm bao gồm cả mã này phải được thay đổi. Chi phí này tất nhiên là không đổi, vì vậy người ta có thể tranh luận, rằng nó không thực sự quan trọng. Trên thực tế, trong các ngôn ngữ như Ruby, nó thực sự không so sánh với lợi ích, nhưng được đưa ra cách lập trình chức năng gõ tĩnh bao gồm rất nhiều bài kiểm tra đơn vị mặt đất được dự định, có vẻ như đó là một chi phí liên tục có thể giảm mà không bị phạt.

Từ đó tôi suy ra rằng các bài kiểm tra đơn vị có phần lỗi thời trong phong cách lập trình này. Tất nhiên, một tuyên bố như vậy chỉ có thể dẫn đến các cuộc chiến tôn giáo, vì vậy hãy để tôi giải thích vấn đề này đơn giản:

Khi bạn sử dụng một kiểu lập trình như vậy, bạn sẽ sử dụng các bài kiểm tra đơn vị ở mức độ nào và tại sao (bạn hy vọng đạt được chất lượng nào cho mã của mình)? Hoặc ngược lại: bạn có tiêu chí nào để bạn có thể đủ điều kiện cho một đơn vị mã chức năng được nhập tĩnh như được phân tích bởi bộ phân tích tĩnh và do đó không cần bảo hiểm kiểm tra đơn vị không?


4
Nhân tiện, nếu bạn chưa thử QuickCheck , bạn chắc chắn nên.
Jon Purdy

scalacheck.org là tương đương Scala
V-Lamp

Câu trả lời:


8

Một khía cạnh của các bài kiểm tra đơn vị là có các thông số kỹ thuật ở dạng có thể chạy được. Tuy nhiên, khi sử dụng một kiểu khai báo, ánh xạ trực tiếp các thông số kỹ thuật chính thức sang ngữ nghĩa ngôn ngữ, thậm chí có thể thực sự thể hiện các thông số kỹ thuật ở dạng có thể chạy theo một cách riêng biệt, điều đó làm tăng giá trị?

Nếu bạn có thông số kỹ thuật có thể được ánh xạ trực tiếp đến khai báo hàm - tốt. Nhưng thông thường, đó là hai mức độ trừu tượng hoàn toàn khác nhau. Các thử nghiệm đơn vị nhằm kiểm tra các đoạn mã đơn, được viết dưới dạng thử nghiệm hộp trắng bởi cùng một nhà phát triển đang làm việc trên hàm. Thông số kỹ thuật thường trông giống như "khi tôi nhập giá trị này vào đây và nhấn vào nút này, điều này sẽ xảy ra". Thông thường một thông số kỹ thuật như vậy dẫn đến nhiều hơn một chức năng được phát triển và thử nghiệm.

Tuy nhiên, một lỗi đơn giản như sử dụng x thay vì y (cả hai đều là tọa độ) trong mã của bạn không thể được bảo vệ. Tuy nhiên, một lỗi như vậy cũng có thể phát sinh trong khi viết mã kiểm tra, vì vậy tôi không chắc liệu nó có đáng để nỗ lực hay không.

Quan niệm sai lầm của bạn là ở đây rằng các bài kiểm tra đơn vị thực sự là để tìm lỗi trong mã của bạn - điều đó không đúng, ít nhất, nó chỉ đúng một phần. Chúng được tạo ra để ngăn bạn giới thiệu các lỗi sau này khi mã của bạn phát triển. Vì vậy, khi bạn lần đầu tiên kiểm tra chức năng và kiểm tra đơn vị của bạn hoạt động (với "x" và "y" đúng chỗ), và sau đó trong khi tái cấu trúc bạn sử dụng x thay vì y, thì kiểm tra đơn vị sẽ hiển thị cho bạn.

Các thử nghiệm đơn vị thực hiện giới thiệu dự phòng, có nghĩa là khi các yêu cầu thay đổi, mã thực hiện chúng và các thử nghiệm bao gồm cả mã này phải được thay đổi. Tất nhiên chi phí này là không đổi, vì vậy người ta có thể tranh luận, rằng nó không thực sự quan trọng. Trên thực tế, trong các ngôn ngữ như Ruby, nó thực sự không so sánh với lợi ích, nhưng được đưa ra cách lập trình chức năng gõ tĩnh bao gồm rất nhiều bài kiểm tra đơn vị mặt đất được dự định, có vẻ như đó là một chi phí liên tục có thể giảm mà không bị phạt.

Trong kỹ thuật, hầu hết các hệ thống an toàn đều dựa vào sự dư thừa. Ví dụ, hai lần nghỉ trong một chiếc ô tô, một chiếc dù dự phòng cho một thợ lặn trên bầu trời, vv Ý tưởng tương tự áp dụng cho các bài kiểm tra đơn vị. Tất nhiên, có nhiều mã để thay đổi khi các yêu cầu thay đổi có thể là một bất lợi. Vì vậy, đặc biệt trong các bài kiểm tra đơn vị, điều quan trọng là giữ cho họ KHÔ (tuân theo nguyên tắc "Đừng lặp lại chính mình"). Trong một ngôn ngữ gõ tĩnh, bạn có thể phải viết một số bài kiểm tra đơn vị ít hơn so với ngôn ngữ được gõ yếu. Đặc biệt các bài kiểm tra "chính thức" có thể không cần thiết - đó là một điều tốt, vì nó cho bạn nhiều thời gian hơn để làm bài kiểm tra đơn vị quan trọng kiểm tra những điều quan trọng. Và đừng nghĩ chỉ vì bạn loại tĩnh, bạn không cần kiểm tra đơn vị, vẫn còn nhiều chỗ để giới thiệu lỗi trong khi tái cấu trúc.


5

Một khía cạnh của các bài kiểm tra đơn vị là có các thông số kỹ thuật ở dạng có thể chạy được. Tuy nhiên, khi sử dụng một kiểu khai báo, ánh xạ trực tiếp các thông số kỹ thuật chính thức sang ngữ nghĩa ngôn ngữ, thậm chí có thể thực sự thể hiện các thông số kỹ thuật ở dạng có thể chạy theo một cách riêng biệt, điều đó làm tăng giá trị?

Rất có khả năng là bạn sẽ không thể thể hiện hoàn toàn các thông số kỹ thuật của mình dưới dạng các ràng buộc kiểu.

Khi bạn sử dụng một kiểu lập trình như vậy, bạn sẽ sử dụng các bài kiểm tra đơn vị ở mức độ nào và tại sao (bạn hy vọng đạt được chất lượng nào cho mã của mình)?

Trên thực tế, một lợi ích chính của phong cách này là các hàm thuần túy dễ kiểm tra đơn vị hơn: không cần thiết lập trạng thái bên ngoài hoặc kiểm tra nó sau khi thực hiện.

Thông thường đặc tả (hoặc một phần của hàm) của hàm có thể được biểu diễn dưới dạng các thuộc tính liên quan đến giá trị được trả về cho các đối số. Trong trường hợp này, sử dụng QuickCheck (cho Haskell) hoặc ScalaCheck (cho Scala) có thể cho phép bạn viết ra các thuộc tính này dưới dạng biểu thức của ngôn ngữ và kiểm tra xem chúng có giữ các đầu vào ngẫu nhiên không.


1
Chi tiết hơn một chút về QuickCheck: ý tưởng cơ bản là bạn viết ra "thuộc tính" (bất biến trong mã của bạn) và chỉ định cách tạo đầu vào tiềm năng. Quickcheck sau đó tạo ra một tấn đầu vào ngẫu nhiên và đảm bảo rằng bất biến của bạn giữ trong mọi trường hợp. Nó là kỹ lưỡng hơn so với thử nghiệm đơn vị.
Tikhon Jelvis

1

Bạn có thể nghĩ về các bài kiểm tra đơn vị như một ví dụ về cách sử dụng mã, cùng với một mô tả về lý do tại sao nó có giá trị.

Đây là một ví dụ, trong đó chú Bob rất tốt bụng khi cặp với tôi trong "Trò chơi cuộc sống" của John Conway . Tôi nghĩ rằng đó là một bài tập tuyệt vời cho loại điều này. Hầu hết các bài kiểm tra là toàn hệ thống, kiểm tra toàn bộ trò chơi, nhưng bài kiểm tra đầu tiên chỉ kiểm tra một chức năng - bài kiểm tra tính toán các hàng xóm xung quanh một ô. Bạn có thể thấy rằng tất cả các bài kiểm tra được viết dưới dạng khai báo, với hành vi chúng tôi đang tìm kiếm được đánh vần rõ ràng.

Cũng có thể giả định các chức năng được sử dụng trong các chức năng; hoặc bằng cách chuyển chúng vào hàm (tương đương với phép tiêm phụ thuộc) hoặc với các khung như Midje của Brian Marick .


0

Có, các bài kiểm tra đơn vị đã có ý nghĩa với mã chức năng được nhập tĩnh. Một ví dụ đơn giản:

prop_encode a = (decode . encode $ a) == a

Bạn có thể buộc prop_encodevới loại tĩnh.

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.