Tại sao không kiểm tra ngôn ngữ một tính năng được hỗ trợ ở cấp cú pháp?


37

Bạn có thể tìm thấy một danh sách vô tận các blog, bài viết và trang web quảng bá lợi ích của việc kiểm tra đơn vị mã nguồn của bạn. Gần như đảm bảo rằng các nhà phát triển đã lập trình trình biên dịch cho Java, C ++, C # và các ngôn ngữ được nhập khác đã sử dụng thử nghiệm đơn vị để xác minh công việc của họ.

Vậy tại sao sau đó, mặc dù phổ biến, nhưng thử nghiệm vắng mặt trong cú pháp của các ngôn ngữ này?

Microsoft đã giới thiệu LINQ cho C # , vậy tại sao họ cũng không thể thêm thử nghiệm?

Tôi không muốn dự đoán những thay đổi ngôn ngữ đó sẽ là gì, nhưng để làm rõ lý do tại sao chúng vắng mặt để bắt đầu.

Ví dụ: Chúng tôi biết rằng bạn có thể viết một forvòng lặp mà không cần cú pháp của forcâu lệnh. Bạn có thể sử dụng whilehoặc if/ gototuyên bố. Ai đó đã quyết định một fortuyên bố là hiệu quả hơn và đưa nó vào một ngôn ngữ.

Tại sao không thử nghiệm theo cùng sự phát triển của ngôn ngữ lập trình?


24
Có thực sự tốt hơn để làm cho một đặc tả ngôn ngữ phức tạp hơn bằng cách thêm cú pháp, trái ngược với việc thiết kế một ngôn ngữ với các quy tắc đơn giản có thể dễ dàng được mở rộng theo cách chung chung?
KChaloux

6
Bạn có ý nghĩa gì bởi "ở cấp độ cú pháp"? Bạn có bất kỳ chi tiết / ý tưởng về cách nó sẽ được thực hiện?
SJuan76

21
Bạn có thể đưa ra một ví dụ mã giả về thử nghiệm như một tính năng được hỗ trợ cú pháp không? Tôi tò mò muốn xem bạn nghĩ nó sẽ như thế nào.
Thất vọngWithFormsDesigner

12
@FrustratedWithFormsDesigner Xem ngôn ngữ D để biết ví dụ.
Doval

4
Một ngôn ngữ khác với các tính năng kiểm tra đơn vị tích hợp là Rust.
Sebastian Redl

Câu trả lời:


36

Như với nhiều thứ, kiểm tra đơn vị được hỗ trợ tốt nhất ở cấp thư viện, không phải cấp độ ngôn ngữ. Đặc biệt, C # có sẵn nhiều thư viện Kiểm thử đơn vị, cũng như những thứ có nguồn gốc từ .NET Framework như thế nào Microsoft.VisualStudio.TestTools.UnitTesting.

Mỗi thư viện Kiểm thử đơn vị có một triết lý và cú pháp kiểm tra hơi khác nhau. Tất cả mọi thứ đều bình đẳng, nhiều lựa chọn tốt hơn ít hơn. Nếu kiểm tra đơn vị được đưa vào ngôn ngữ, bạn sẽ bị khóa trong các lựa chọn của nhà thiết kế ngôn ngữ hoặc bạn sẽ sử dụng ... thư viện và tránh hoàn toàn các tính năng kiểm tra ngôn ngữ.

Ví dụ

  • Nunit - Mục đích chung, khung thử nghiệm đơn vị được thiết kế thành ngữ, tận dụng tối đa các tính năng ngôn ngữ của C #.

  • Moq - Khung mô phỏng tận dụng tối đa lợi thế của biểu thức lambda và cây biểu thức, không có ẩn dụ ghi / phát lại.

Có nhiều sự lựa chọn khác. Các thư viện như Microsoft Fakes có thể tạo ra các "shims ..." giả không yêu cầu bạn viết các lớp của mình bằng các giao diện hoặc phương thức ảo.

Linq không phải là một tính năng ngôn ngữ (mặc dù tên của nó)

Linq là một tính năng thư viện. Chúng tôi đã nhận được rất nhiều tính năng mới trong chính ngôn ngữ C #, như các biểu thức lambda và các phương thức mở rộng, nhưng việc triển khai thực tế của Linq là trong .NET Framework.

Có một số đường cú pháp đã được thêm vào C # để làm cho câu lệnh linq sạch hơn, nhưng đường đó không bắt buộc phải sử dụng linq.


1
Tôi đồng ý với quan điểm rằng LINQ không phải là một tính năng ngôn ngữ nhưng để làm cho LINQ xảy ra, cần phải có một số tính năng ngôn ngữ cơ bản xảy ra - đặc biệt là các loại tổng quát và động.
Wyatt Barnett

@WyattBarnett: Có, và điều tương tự cũng đúng đối với thử nghiệm đơn vị dễ dàng - như phản xạ và thuộc tính.
Doc Brown

8
LINQ cần lambdas và cây biểu hiện. Các thế hệ đã có trong C # (và .Net nói chung, chúng có mặt trong CLR) và động lực học không liên quan gì đến LINQ; bạn có thể làm LINQ mà không cần năng động.
Arthur van Leeuwen

Perl's DBIx :: Class là một ví dụ về chức năng giống như LINQ được triển khai hơn 10 năm sau khi tất cả các tính năng cần thiết để triển khai nó đã có trong ngôn ngữ.
slebetman 04

2
@ Carson63000 Tôi nghĩ bạn có nghĩa là các loại ẩn danh kết hợp với vartừ khóa :)
M.Mimpen

21

Có rất nhiều lý do. Eric Lippert đã tuyên bố nhiều lần rằng lý do feature Xkhông có trong C # là vì nó không nằm trong ngân sách của họ. Các nhà thiết kế ngôn ngữ không có thời gian vô hạn cũng như tiền để thực hiện mọi thứ và mỗi tính năng mới có chi phí bảo trì liên quan đến nó. Giữ ngôn ngữ càng nhỏ càng tốt, không chỉ dễ dàng hơn đối với các nhà thiết kế ngôn ngữ - mọi người cũng dễ dàng hơn khi viết các công cụ và triển khai thay thế (ví dụ IDE) Ngoài ra, khi một thứ gì đó được triển khai theo ngôn ngữ chứ không phải là một phần của ngôn ngữ tính di động miễn phí. Nếu kiểm thử đơn vị đang triển khai như một thư viện, bạn chỉ cần viết nó một lần và nó sẽ hoạt động trong bất kỳ triển khai ngôn ngữ phù hợp nào.

Điều đáng chú ý là D có hỗ trợ cấp cú pháp để kiểm tra đơn vị . Tôi không biết tại sao họ quyết định ném nó vào, nhưng đáng chú ý rằng D có nghĩa là một "ngôn ngữ lập trình hệ thống cấp cao". Các nhà thiết kế muốn nó khả thi đối với loại mã C ++ không an toàn, mức độ thấp thường được sử dụng và một lỗi trong mã không an toàn là rất tốn kém - hành vi không xác định. Vì vậy, tôi cho rằng việc họ dành nhiều nỗ lực hơn cho bất cứ điều gì giúp bạn xác minh rằng một số mã không an toàn có hiệu quả. Ví dụ, bạn có thể thực thi rằng chỉ những mô-đun đáng tin cậy nhất định mới có thể thực hiện các hoạt động không an toàn như truy cập mảng không được kiểm soát hoặc số học con trỏ.

Phát triển nhanh cũng là ưu tiên hàng đầu của họ, đến nỗi họ đã đặt mục tiêu thiết kế mà mã D biên dịch đủ nhanh để biến nó thành ngôn ngữ kịch bản. Đơn vị nướng thử nghiệm ngay ngôn ngữ để bạn có thể chạy thử nghiệm của mình bằng cách chuyển một cờ phụ cho trình biên dịch giúp với điều đó.

Tuy nhiên, tôi nghĩ rằng một thư viện thử nghiệm đơn vị tuyệt vời làm được nhiều việc hơn là chỉ tìm một số phương thức và chạy chúng. Lấy ví dụ QuickCheck của Haskell , cho phép bạn kiểm tra những thứ như "cho tất cả x và y f (x, y) == f (y, x)". QuickCheck được mô tả tốt hơn dưới dạng trình tạo thử nghiệm đơn vị và cho phép bạn kiểm tra mọi thứ ở mức cao hơn "đối với đầu vào này, tôi đang mong đợi đầu ra này". QuickCheck và Linq không khác nhau nhiều - chúng đều là ngôn ngữ dành riêng cho tên miền. Vì vậy, thay vì hỗ trợ kiểm tra đơn vị hỗ trợ cho một ngôn ngữ, tại sao không thêm các tính năng cần thiết để làm cho DSL thực tế? Bạn sẽ kết thúc với không chỉ kiểm tra đơn vị, mà kết quả là một ngôn ngữ tốt hơn.


3
Để tồn tại trong thế giới .Net, có FsCheck, là một cổng của QuickCheck đến F #, với một số trợ giúp để sử dụng từ C #.
Arthur van Leeuwen

Để ở trong thế giới .NET? Câu hỏi này không liên quan gì đến .NET.
Miles Rout

@MilesRout C # là một phần của .NET. Câu hỏi và câu trả lời nói về C # thường xuyên, và câu hỏi thậm chí còn nói về LINQ.
Zachary Dow

Câu hỏi không được gắn thẻ .NET hoặc C #.
Miles Rout

@MilesRout Điều đó có thể đúng, nhưng bạn không thể nói một cách hợp lý rằng nó không liên quan gì đến nó chỉ vì nó không có thẻ. :)
Zachary Dow

13

Bởi vì thử nghiệm, và đặc biệt là phát triển dựa trên thử nghiệm, là một hiện tượng phản trực giác sâu sắc.

Hầu như mọi lập trình viên bắt đầu sự nghiệp của họ tin rằng họ giỏi quản lý sự phức tạp hơn nhiều so với thực tế. Việc ngay cả lập trình viên vĩ đại nhất cũng không thể viết các chương trình lớn và phức tạp mà không có lỗi nghiêm trọng trừ khi họ sử dụng nhiều bài kiểm tra hồi quy là điều đáng thất vọng nghiêm trọng và thậm chí đáng xấu hổ đối với nhiều học viên. Do đó, sự không phù hợp phổ biến so với thử nghiệm thường xuyên ngay cả trong số các chuyên gia, những người đã biết rõ hơn.

Tôi nghĩ rằng việc thử nghiệm tôn giáo đang dần trở nên chính thống và được mong đợi phần lớn là do sự bùng nổ về khả năng lưu trữ và sức mạnh tính toán, các hệ thống lớn hơn bao giờ hết được xây dựng - và các hệ thống cực kỳ lớn đặc biệt dễ bị sụp đổ phức tạp. không thể được quản lý mà không có mạng lưới an toàn của các bài kiểm tra hồi quy. Do đó, ngay cả các nhà phát triển đặc biệt cố chấp và si mê hiện thừa nhận một cách miễn cưỡng rằng họ cần thử nghiệm và sẽ luôn cần thử nghiệm (nếu điều này nghe giống như lời thú nhận tại một cuộc họp ở AA, điều này khá có chủ ý - cần một mạng lưới an toàn rất khó chấp nhận đối với nhiều người cá nhân).

Hầu hết các ngôn ngữ phổ biến ngày nay bắt đầu từ trước khi thay đổi thái độ này, vì vậy chúng có ít hỗ trợ tích hợp cho các bài kiểm tra: chúng có assert, nhưng không có hợp đồng. Tôi khá chắc chắn rằng nếu xu hướng vẫn tồn tại, các ngôn ngữ trong tương lai sẽ có nhiều hỗ trợ hơn về ngôn ngữ hơn là cấp độ thư viện.


3
Bạn nói quá trường hợp của bạn một chút. Mặc dù thử nghiệm đơn vị là không thể nghi ngờ là rất quan trọng, xây dựng chương trình theo cách phù hợp , cách giảm thiểu sự phức tạp không cần thiết, cũng quan trọng không kém, nếu không muốn nói là như vậy. Các ngôn ngữ như Haskell có các hệ thống loại cải thiện độ tin cậy và khả năng chứng minh của các chương trình được viết trong đó, và các thực tiễn tốt nhất về mã hóa và thiết kế tránh được nhiều cạm bẫy của mã chưa được kiểm tra, khiến việc kiểm tra đơn vị trở nên ít quan trọng hơn so với cách khác.
Robert Harvey

1
@RobertHarve ... và thử nghiệm đơn vị là một cách rất tốt để gián tiếp thúc đẩy các nhà phát triển hướng tới điều đó. Làm việc trên API trong khi tạo các bài kiểm tra, thay vì sử dụng nó với mã khác, giúp đặt chúng vào tư duy đúng để hạn chế hoặc loại bỏ trừu tượng bị rò rỉ hoặc các cuộc gọi phương thức kỳ quặc. Tôi không nghĩ KillianFoth đang cường điệu hóa bất cứ điều gì.
Izkata

@Robert: Điều duy nhất tôi tin rằng anh ta nói quá là "thử nghiệm tôn giáo đang dần trở nên chính thống hơn" Nếu chậm được định nghĩa theo các khung thời gian Địa lý, anh ta có lẽ không còn xa nữa ..... :)
mattnz

+1 Trong công việc hiện tại của tôi, tôi bị ảnh hưởng bởi sự thay đổi của thủy triều trong thái độ phát triển với các thư viện và công nghệ mới hơn có sẵn. Lần đầu tiên trong sự nghiệp, những người này đang dạy tôi sử dụng quy trình phát triển tinh vi hơn, thay vì tôi cảm thấy như mình đang ở trên đỉnh đồi hét gió. Tôi đồng ý chỉ là vấn đề thời gian trước khi những thái độ này tìm thấy biểu hiện trong cú pháp ngôn ngữ bản địa.
Rob

1
Xin lỗi, thêm một suy nghĩ nữa: bình luận của Robert Harvey. Hiện tại các hệ thống loại là một kiểm tra tốt, nhưng thực sự thiếu đáng kể khi xác định các ràng buộc đối với các loại - chúng giải quyết giao diện, nhưng không hạn chế hành vi chính xác. (tức là 1 + 1 == 3 sẽ biên dịch, nhưng có thể đúng hoặc không, tùy thuộc vào việc triển khai +) Tôi tự hỏi liệu xu hướng tiếp theo sẽ thấy các hệ thống loại biểu cảm hơn kết hợp những gì chúng ta coi là thử nghiệm đơn vị bây giờ.
Rob

6

Rất nhiều ngôn ngữ có hỗ trợ để thử nghiệm. Khẳng định của C là các bài kiểm tra mà chương trình có thể thất bại. Đó là nơi hầu hết các ngôn ngữ dừng lại, nhưng Eiffel và gần đây là Ada 2012 có các tiền tố (điều mà các đối số của hàm phải vượt qua) và postinvariants (những thứ mà đầu ra của hàm phải vượt qua), với Ada cung cấp khả năng tham chiếu các đối số ban đầu trong hậu biến. Ada 2012 cũng cung cấp các bất biến loại, do đó, bất cứ khi nào một phương thức được gọi trên một lớp Ada, loại bất biến được kiểm tra trước khi trả về.

Đó không phải là loại thử nghiệm toàn diện mà khung thử nghiệm tốt có thể cung cấp cho bạn, nhưng đó là loại thử nghiệm quan trọng mà các ngôn ngữ có thể hỗ trợ tốt nhất.


2
Đã từng làm một chút công việc ở Eiffel và là một người sử dụng rộng rãi các xác nhận, kinh nghiệm của tôi là bất cứ điều gì bạn có thể làm theo hợp đồng trong tự nhiên đều giúp loại bỏ rất nhiều lỗi.
Blrfl

2

Một số người đề xuất các ngôn ngữ chức năng được đánh máy mạnh sẽ cho rằng các tính năng ngôn ngữ này làm giảm hoặc loại bỏ nhu cầu kiểm tra đơn vị.

Hai, imho, ví dụ điển hình cho việc này là từ F # cho Vui và Lợi nhuận ở đâyđây

Cá nhân, tôi vẫn tin vào giá trị của các bài kiểm tra đơn vị, nhưng có một số điểm hợp lệ. Ví dụ, nếu một trạng thái bất hợp pháp là không thể trình bày được trong mã, thì không chỉ không cần thiết phải viết một bài kiểm tra đơn vị cho trường hợp này, điều đó là không thể.


2

Tôi cho rằng bạn đã bỏ lỡ việc bổ sung một số tính năng cần thiết vì chúng không được làm nổi bật như để thử nghiệm đơn vị .

Ví dụ, kiểm tra đơn vị trong C # chủ yếu được điều khiển bằng cách sử dụng các thuộc tính. Các tuỳ chỉnh thuộc tính năng cung cấp một cơ chế mở rộng phong phú cho phép các khuôn khổ như NUnit để lặp và cạnh tranh, với những thứ như lý thuyết dựa trêntham số thử nghiệm.

Điều này đưa tôi đến điểm quan trọng thứ hai của mình - chúng ta không biết đủ về những gì làm cho khả năng kiểm tra tốt để đưa nó vào ngôn ngữ. Tốc độ đổi mới trong thử nghiệm nhanh hơn nhiều so với các cấu trúc ngôn ngữ khác, vì vậy chúng ta cần có các cơ chế linh hoạt trong ngôn ngữ của mình để có thể tự do đổi mới.

Tôi là người dùng nhưng không cuồng tín về TDD - nó rất hữu ích trong một số trường hợp đặc biệt là để cải thiện tư duy thiết kế của bạn. Nó không nhất thiết hữu ích cho các hệ thống kế thừa lớn hơn - thử nghiệm cấp cao hơn với dữ liệu tốt và tự động hóa có thể sẽ mang lại nhiều lợi ích hơn cùng với văn hóa kiểm tra mã mạnh mẽ .

Bài đọc khác có liên quan:

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.