it-swarm-vi.com

Là khẳng định hoặc kiểm tra đơn vị quan trọng hơn?

Cả hai khẳng định và kiểm tra đơn vị đều đóng vai trò là tài liệu cho một cơ sở mã hóa và là phương tiện để phát hiện ra các lỗi. Sự khác biệt chính là khẳng định chức năng như kiểm tra độ tỉnh táo và xem các đầu vào thực, trong khi các thử nghiệm đơn vị chạy trên các đầu vào mô phỏng cụ thể và là các thử nghiệm đối với một "câu trả lời đúng" được xác định rõ. Các giá trị tương đối của việc sử dụng các khẳng định so với các bài kiểm tra đơn vị là phương tiện chính để xác minh tính đúng đắn là gì? Mà bạn tin rằng nên được nhấn mạnh hơn?

52
dsimcha

Asserts rất hữu ích để cho bạn biết về trạng thái bên trong của chương trình. Ví dụ: cấu trúc dữ liệu của bạn có trạng thái hợp lệ, ví dụ: cấu trúc dữ liệu Time sẽ không giữ giá trị của 25:61:61. Các điều kiện được kiểm tra bởi các khẳng định là:

  • Điều kiện tiên quyết, đảm bảo rằng người gọi giữ hợp đồng của mình,

  • Postconditions, đảm bảo rằng callee giữ hợp đồng của nó, và

  • Các bất biến, đảm bảo rằng cấu trúc dữ liệu luôn giữ một số thuộc tính sau khi hàm trả về. Một bất biến là một điều kiện là điều kiện tiên quyết và hậu điều kiện.

Kiểm tra đơn vị rất hữu ích để cho bạn biết về hành vi bên ngoài của mô-đun. Stack của bạn có thể có trạng thái nhất quán sau khi phương thức Push() được gọi, nhưng nếu kích thước của ngăn xếp không tăng gấp ba sau khi nó được gọi ba lần, thì đó là một lỗi . (Ví dụ: trường hợp tầm thường trong đó việc triển khai Push() không chính xác chỉ kiểm tra các xác nhận và thoát.)

Nói một cách chính xác, sự khác biệt chính giữa các xác nhận và kiểm tra đơn vị là kiểm tra đơn vị có dữ liệu kiểm tra (các giá trị để chương trình chạy), trong khi các xác nhận thì không. Đó là, bạn có thể thực hiện các bài kiểm tra đơn vị của mình một cách tự động, trong khi bạn không thể nói tương tự cho các xác nhận. Vì lợi ích của cuộc thảo luận này, tôi đã giả định rằng bạn đang nói về việc thực hiện chương trình trong bối cảnh kiểm tra chức năng bậc cao (thực thi toàn bộ chương trình và không lái các mô-đun như kiểm tra đơn vị). Nếu bạn không nói về kiểm tra chức năng tự động như là phương tiện để "xem đầu vào thực", thì rõ ràng giá trị nằm ở tự động hóa, và do đó, kiểm tra đơn vị sẽ giành chiến thắng. Nếu bạn đang nói về điều này trong bối cảnh kiểm tra chức năng (tự động), thì xem bên dưới.

Có thể có một số chồng chéo trong những gì đang được thử nghiệm. Ví dụ, postcondition của Stack thực sự có thể khẳng định rằng kích thước ngăn xếp tăng thêm một. Nhưng có những giới hạn đối với những gì có thể được thực hiện trong khẳng định đó: Nó cũng nên kiểm tra xem phần tử trên cùng có phải là những gì vừa được thêm vào không?

Đối với cả hai, mục tiêu là tăng chất lượng. Đối với thử nghiệm đơn vị, mục tiêu là tìm lỗi. Đối với các xác nhận, mục tiêu là làm cho việc gỡ lỗi dễ dàng hơn bằng cách quan sát các trạng thái chương trình không hợp lệ ngay khi chúng xảy ra.

Lưu ý rằng kỹ thuật không xác minh tính chính xác. Trong thực tế, nếu bạn tiến hành kiểm tra đơn vị với mục tiêu để xác minh chương trình là chính xác, bạn có thể sẽ đưa ra thử nghiệm không thú vị mà bạn biết sẽ hoạt động. Đó là một hiệu ứng tâm lý: bạn sẽ làm bất cứ điều gì để đáp ứng mục tiêu của bạn. Nếu mục tiêu của bạn là tìm lỗi, các hoạt động của bạn sẽ phản ánh điều đó.

Cả hai đều quan trọng, và có mục đích riêng của họ.

[Là một lưu ý cuối cùng về các xác nhận: Để có được giá trị cao nhất, bạn cần sử dụng chúng tại tất cả các điểm quan trọng trong chương trình của bạn và không phải là một vài chức năng chính. Mặt khác, nguồn gốc của vấn đề có thể đã bị che giấu và khó phát hiện mà không cần gỡ lỗi hàng giờ.]

43
Macneil

Khi nói về các xác nhận, hãy nhớ rằng chúng có thể được tắt ngay lập tức.

Ví dụ về một khẳng định rất xấu:

char *c = malloc(1024);
assert(c != NULL);

Tại sao điều này là xấu? Bởi vì không có kiểm tra lỗi được thực hiện nếu xác nhận đó bị bỏ qua do một cái gì đó giống như NDEBUG được xác định.

Một bài kiểm tra đơn vị sẽ (có thể) chỉ là segfault trên đoạn mã trên. Chắc chắn, nó đã làm công việc của nó bằng cách nói với bạn rằng một cái gì đó đã đi sai, hoặc đã làm nó? malloc() có khả năng thất bại như thế nào trong bài kiểm tra?

Các xác nhận là dành cho mục đích gỡ lỗi khi lập trình viên cần ngụ ý rằng không có sự kiện 'bình thường' nào sẽ khiến cho xác nhận được kích hoạt. malloc() failing, thực sự là một sự kiện bình thường, do đó không bao giờ nên được khẳng định.

Có nhiều trường hợp khác mà các xác nhận được sử dụng thay vì xử lý thỏa đáng những điều có thể sai. Đây là lý do tại sao các xác nhận có tiếng xấu và tại sao các ngôn ngữ như Go không bao gồm chúng.

Các bài kiểm tra đơn vị được thiết kế để cho bạn biết khi nào một thứ bạn thay đổi đã phá vỡ thứ khác. Chúng được thiết kế để giúp bạn (trong hầu hết các trường hợp) tránh khỏi mọi tính năng của chương trình (tuy nhiên, người kiểm tra quan trọng đối với các bản phát hành) mỗi khi bạn tạo bản dựng.

Thực sự không có bất kỳ mối tương quan rõ rệt nào giữa hai người, ngoài cả hai đều nói với bạn rằng có điều gì đó không ổn. Hãy nghĩ về một khẳng định như một điểm dừng trong một cái gì đó bạn đang làm việc, mà không phải sử dụng một trình gỡ lỗi. Hãy nghĩ về một bài kiểm tra đơn vị như một cái gì đó cho bạn biết nếu bạn đã phá vỡ thứ gì đó mà bạn không làm việc.

7
Tim Post

Chúng là cả hai công cụ được sử dụng để giúp cải thiện chất lượng tổng thể của hệ thống bạn đang xây dựng. Rất nhiều tùy thuộc vào ngôn ngữ bạn đang sử dụng, loại ứng dụng bạn đang xây dựng và thời gian bạn sử dụng tốt nhất. Chưa kể bạn có một vài trường phái suy nghĩ về nó.

Để bắt đầu, nếu bạn đang sử dụng ngôn ngữ không có từ khóa assert, bạn không thể sử dụng các xác nhận (ít nhất là không theo cách chúng ta đang nói ở đây). Trong một thời gian dài, Java không có từ khóa assert và một số ngôn ngữ vẫn không có. Kiểm tra đơn vị sau đó trở nên quan trọng hơn một chút. Trong một số ngôn ngữ các xác nhận chỉ được chạy khi cờ được đặt (một lần nữa với Java ở đây). Khi các biện pháp bảo vệ không phải lúc nào cũng ở đó, đó không phải là một tính năng rất hữu ích.

Có một trường phái cho rằng nếu bạn đang "khẳng định" điều gì đó, bạn cũng có thể viết một if/throw khối ngoại lệ có ý nghĩa. Quá trình suy nghĩ này xuất phát từ nhiều khẳng định được đặt vào đầu một phương thức để đảm bảo tất cả các giá trị nằm trong giới hạn. Kiểm tra các điều kiện tiên quyết của bạn là một phần rất quan trọng để có một postcondition dự kiến.

Kiểm thử đơn vị là mã bổ sung phải được viết và duy trì. Đối với nhiều người đây là một nhược điểm. Tuy nhiên, với các khung kiểm thử đơn vị hiện tại, bạn có thể tạo ra số lượng điều kiện kiểm thử lớn hơn với mã tương đối ít. Các thử nghiệm được tham số hóa và "lý thuyết" sẽ thực hiện cùng một thử nghiệm với một số lượng lớn các mẫu dữ liệu có thể phát hiện ra một số lỗi khó tìm.

Cá nhân tôi thấy rằng tôi nhận được nhiều dặm hơn với thử nghiệm đơn vị hơn là rắc các xác nhận, nhưng đó là do các nền tảng tôi phát triển trên hầu hết thời gian (Java/C #). Các ngôn ngữ khác có hỗ trợ xác nhận mạnh mẽ hơn và thậm chí "Thiết kế theo hợp đồng" (xem bên dưới) để cung cấp nhiều đảm bảo hơn nữa. Nếu tôi đang làm việc với một trong những ngôn ngữ này, tôi có thể sử dụng DBC nhiều hơn thử nghiệm đơn vị.

http://en.wikipedia.org/wiki/Design_by_contract#Lacular_with_native_support

5
Berin Loritsch