Có đáng để viết bài kiểm tra đơn vị cho mã nghiên cứu khoa học?


89

Tôi rất tin tưởng vào giá trị của việc sử dụng các bài kiểm tra xác minh một chương trình hoàn chỉnh (ví dụ: các bài kiểm tra hội tụ), bao gồm một tập các bài kiểm tra hồi quy tự động . Sau khi đọc một số sách lập trình, tôi đã nhận được cảm giác cằn nhằn rằng tôi "phải" viết các bài kiểm tra đơn vị (nghĩa là các bài kiểm tra xác minh tính đúng đắn của một hàm và cũng không chạy toàn bộ mã để giải quyết vấn đề) . Tuy nhiên, các bài kiểm tra đơn vị dường như không luôn phù hợp với các quy tắc khoa học và cuối cùng cảm thấy giả tạo hoặc giống như một sự lãng phí thời gian.

Chúng ta có nên viết bài kiểm tra đơn vị cho mã nghiên cứu?


2
Đây là một chút của một câu hỏi mở, phải không?
qubyte

2
Như với tất cả các "quy tắc", một liều tư duy phê phán luôn được áp dụng. Tự hỏi bản thân nếu một thói quen nhất định có một cách rõ ràng để được kiểm tra đơn vị. Nếu không, thì một bài kiểm tra đơn vị sẽ không có ý nghĩa tại thời điểm đó, hoặc thiết kế của mã kém. Lý tưởng nhất, một thói quen thực hiện một nhiệm vụ độc lập với các thói quen khác càng tốt, nhưng điều đó đôi khi phải đánh đổi.
Lagerbaer

Có một số cuộc thảo luận tốt trong một tĩnh mạch tương tự về một câu hỏi trên stackoverflow .
ness101

Câu trả lời:


85

Trong nhiều năm, tôi đã hiểu sai rằng tôi không có đủ thời gian để viết bài kiểm tra đơn vị cho mã của mình. Khi tôi viết bài kiểm tra, chúng là những thứ nặng nề, nặng nề, điều đó chỉ khuyến khích tôi nghĩ rằng tôi chỉ nên viết bài kiểm tra đơn vị khi tôi biết chúng là cần thiết.

Sau đó, tôi bắt đầu sử dụng Test Driven Development và tôi thấy đó là một tiết lộ hoàn chỉnh. Bây giờ tôi tin chắc rằng tôi không có thời gian để không viết bài kiểm tra đơn vị .

Theo kinh nghiệm của tôi, bằng cách phát triển với thử nghiệm trong tâm trí, bạn kết thúc với các giao diện sạch hơn, các lớp và mô-đun tập trung hơn và nói chung là RẮN hơn , mã có thể kiểm tra.

Mỗi lần tôi làm việc với mã kế thừa không có kiểm tra đơn vị và phải kiểm tra thủ công một cái gì đó, tôi cứ nghĩ "việc này sẽ nhanh hơn rất nhiều nếu mã này đã có kiểm tra đơn vị". Mỗi lần tôi phải thử và thêm chức năng kiểm tra đơn vị vào mã có độ khớp cao, tôi cứ nghĩ "việc này sẽ dễ dàng hơn nhiều nếu nó được viết theo cách tách rời".

So sánh và đối chiếu hai trạm thử nghiệm mà tôi hỗ trợ. Một cái đã xuất hiện được một thời gian và có rất nhiều mã kế thừa, trong khi cái còn lại thì tương đối mới.

Khi thêm chức năng vào phòng thí nghiệm cũ, thường là trường hợp xuống phòng thí nghiệm và dành nhiều giờ để làm việc với hàm ý của chức năng họ cần và làm thế nào tôi có thể thêm chức năng đó mà không ảnh hưởng đến bất kỳ chức năng nào khác. Mã đơn giản là không được thiết lập để cho phép thử nghiệm ngoại tuyến, do đó, hầu hết mọi thứ phải được phát triển trực tuyến. Nếu tôi đã cố gắng phát triển ngoại tuyến thì tôi sẽ kết thúc với nhiều đối tượng giả hơn là hợp lý.

Trong phòng thí nghiệm mới hơn, tôi thường có thể thêm chức năng bằng cách phát triển ngoại tuyến tại bàn của mình, chỉ loại bỏ những thứ cần thiết ngay lập tức, và sau đó chỉ dành một thời gian ngắn trong phòng thí nghiệm, loại bỏ mọi vấn đề còn lại không được xử lý -hàng.

Để rõ ràng, và vì @ naught101 đã hỏi ...

Tôi có xu hướng làm việc trên phần mềm kiểm soát dữ liệu và kiểm soát thử nghiệm, với một số phân tích dữ liệu đặc biệt, do đó, việc kết hợp TDD với kiểm soát sửa đổi giúp ghi lại cả những thay đổi trong phần cứng thử nghiệm cơ bản và cũng như thay đổi yêu cầu thu thập dữ liệu theo thời gian.

Tuy nhiên, ngay cả trong tình huống phát triển mã khám phá, tôi có thể thấy một lợi ích đáng kể từ việc các giả định được mã hóa, cùng với khả năng xem các giả định đó phát triển theo thời gian như thế nào.


7
Mark, bạn đang nói về loại mã nào ở đây? Mô hình tái sử dụng? Tôi thấy rằng lý do này không thực sự phù hợp với những thứ như mã phân tích dữ liệu khám phá, nơi bạn thực sự cần phải nhảy nhiều, và thường không bao giờ mong đợi sử dụng lại mã ở bất kỳ nơi nào khác.
ness101

35

Các mã khoa học có xu hướng có các chòm sao của các hàm lồng vào nhau thường xuyên hơn các mã kinh doanh mà tôi đã làm việc, thường là do cấu trúc toán học của vấn đề. Vì vậy, tôi không nghĩ các bài kiểm tra đơn vị cho các chức năng riêng lẻ là rất hiệu quả. Tuy nhiên, tôi nghĩ có một loại bài kiểm tra đơn vị có hiệu quả và vẫn hoàn toàn khác với các bài kiểm tra toàn bộ chương trình ở chỗ chúng nhắm mục tiêu chức năng cụ thể.

Tôi chỉ xác định ngắn gọn những gì tôi có ý nghĩa của các loại thử nghiệm. Kiểm tra hồi quy tìm kiếm các thay đổi trong hành vi hiện có (được xác thực bằng cách nào đó) khi các thay đổi được thực hiện đối với mã. Kiểm thử đơn vị chạy một đoạn mã và kiểm tra xem nó có cho đầu ra mong muốn dựa trên một đặc tả không. Chúng không khác nhau, vì thử nghiệm hồi quy ban đầu thử nghiệm đơn vị vì tôi phải xác định rằng đầu ra là hợp lệ.

Ví dụ yêu thích của tôi về kiểm tra đơn vị số là kiểm tra tốc độ hội tụ của việc thực hiện phần tử hữu hạn. Điều này chắc chắn không đơn giản, nhưng cần một giải pháp đã biết cho PDE, chạy một số vấn đề khi giảm kích thước mắt lưới , và sau đó khớp với chỉ tiêu lỗi cho đường cong C h r trong đó r là tốc độ hội tụ. Tôi làm điều này cho vấn đề Poisson trong PETSc bằng Python. Tôi không tìm kiếm một sự khác biệt, như trong hồi quy, nhưng một tỷ lệ r đặc biệt được chỉ định cho phần tử đã cho.hChrrr

Hai ví dụ nữa về thử nghiệm đơn vị, đến từ PyLith , là vị trí điểm, là một chức năng duy nhất dễ tạo ra kết quả tổng hợp và tạo ra các ô gắn kết khối lượng bằng 0 trong một lưới, bao gồm một số chức năng nhưng giải quyết một phần bị chặn chức năng trong mã.

Có nhiều thử nghiệm loại này, bao gồm các thử nghiệm bảo tồn và nhất quán. Hoạt động không khác với hồi quy (bạn chạy thử nghiệm và kiểm tra đầu ra theo tiêu chuẩn), nhưng đầu ra tiêu chuẩn đến từ một đặc điểm kỹ thuật thay vì chạy trước đó.


4
Wikipedia cho biết "Kiểm thử đơn vị, còn được gọi là kiểm thử thành phần, đề cập đến các kiểm tra xác minh chức năng của một phần mã cụ thể, thường là ở cấp độ chức năng." Các thử nghiệm hội tụ trong mã phần tử hữu hạn rõ ràng không thể là các thử nghiệm đơn vị vì chúng liên quan đến nhiều chức năng.
David Ketcheson

Đó là lý do tại sao tôi nói rõ ở đầu bài rằng tôi có cái nhìn bao quát về các bài kiểm tra đơn vị, và "thường" có nghĩa chính xác là như vậy.
Matt Knepley

Câu hỏi của tôi có nghĩa là theo định nghĩa của các bài kiểm tra đơn vị được chấp nhận rộng rãi hơn. Bây giờ tôi đã thực hiện điều này hoàn toàn rõ ràng trong câu hỏi.
David Ketcheson

Tôi đã làm rõ câu trả lời của mình
Matt Knepley

Các ví dụ sau của bạn có liên quan đến những gì tôi dự định.
David Ketcheson

28

Kể từ khi tôi đọc về Phát triển dựa trên thử nghiệm trong Hoàn thành mã, phiên bản 2 , tôi đã sử dụng khung thử nghiệm đơn vịlà một phần trong chiến lược phát triển của tôi và nó đã tăng đáng kể năng suất của tôi bằng cách giảm lượng thời gian tôi dành cho việc gỡ lỗi vì các thử nghiệm khác nhau tôi viết là chẩn đoán. Là một lợi ích phụ, tôi tin tưởng nhiều hơn vào kết quả khoa học của mình và đã sử dụng các bài kiểm tra đơn vị của mình trong một số trường hợp để bảo vệ kết quả của mình. Nếu có lỗi trong bài kiểm tra đơn vị, tôi thường có thể tìm ra lý do tại sao khá nhanh. Nếu ứng dụng của tôi gặp sự cố và tất cả các bài kiểm tra đơn vị của tôi đều vượt qua, tôi sẽ thực hiện phân tích độ bao phủ mã để xem phần nào trong mã của tôi không được thực hiện, cũng như chuyển qua mã với trình gỡ lỗi để xác định nguồn lỗi. Sau đó, tôi viết một bài kiểm tra mới để đảm bảo rằng lỗi vẫn được sửa.

Nhiều bài kiểm tra tôi viết không phải là bài kiểm tra đơn vị thuần túy. Được xác định nghiêm ngặt, các bài kiểm tra đơn vị được cho là thực hiện chức năng của một chức năng. Khi tôi có thể kiểm tra dễ dàng một chức năng duy nhất bằng cách sử dụng dữ liệu giả, tôi sẽ làm điều đó. Những lần khác, tôi không thể dễ dàng chế nhạo dữ liệu tôi cần để viết bài kiểm tra thực hiện chức năng của một chức năng nhất định, vì vậy tôi sẽ kiểm tra chức năng đó cùng với các chức năng khác trong bài kiểm tra tích hợp. Kiểm tra tích hợpkiểm tra hành vi của nhiều chức năng cùng một lúc. Như Matt chỉ ra, các mã khoa học thường là một chòm sao của các hàm lồng vào nhau, nhưng thường thì các hàm nhất định được gọi theo trình tự và các bài kiểm tra đơn vị có thể được viết để kiểm tra đầu ra ở các bước trung gian. Ví dụ: nếu mã sản xuất của tôi gọi năm hàm theo thứ tự, tôi sẽ viết năm bài kiểm tra. Thử nghiệm đầu tiên sẽ chỉ gọi chức năng đầu tiên (vì vậy đây là thử nghiệm đơn vị). Sau đó, thử nghiệm thứ hai sẽ gọi các chức năng thứ nhất và thứ hai, thử nghiệm thứ ba sẽ gọi ba chức năng đầu tiên, v.v. Ngay cả khi tôi có thể viết các bài kiểm tra đơn vị cho mọi chức năng trong mã của mình, tôi vẫn sẽ viết các bài kiểm tra tích hợp, bởi vì các lỗi có thể phát sinh khi các phần mô-đun khác nhau của một chương trình được kết hợp. Cuối cùng, sau khi viết tất cả các bài kiểm tra đơn vị và kiểm tra tích hợp tôi nghĩ rằng tôi cần, tôi sẽ bao bọc các nghiên cứu trường hợp của tôi trong các bài kiểm tra đơn vị và sử dụng chúng để kiểm tra hồi quy, bởi vì tôi muốn kết quả của mình được lặp lại. Nếu chúng không lặp lại và tôi nhận được kết quả khác nhau, tôi muốn biết tại sao. Thất bại của một bài kiểm tra hồi quy có thể không phải là một vấn đề thực sự, nhưng nó sẽ buộc tôi phải tìm ra nếu kết quả mới ít nhất là đáng tin cậy như kết quả cũ.

Cũng đáng giá cùng với kiểm tra đơn vị là phân tích mã tĩnh, trình gỡ lỗi bộ nhớ và biên dịch với các cờ cảnh báo trình biên dịch để bắt các lỗi đơn giản và mã không sử dụng.



Bạn có xem xét các bài kiểm tra tích hợp đủ không, hoặc bạn có nghĩ rằng bạn cũng cần phải viết các bài kiểm tra đơn vị riêng biệt không?
siamii

Tôi sẽ viết các bài kiểm tra đơn vị riêng biệt ở bất cứ nơi nào có thể và khả thi để làm điều đó. Nó làm cho việc gỡ lỗi dễ dàng hơn và thực thi mã tách rời (đó là những gì bạn muốn).
Geoff Oxberry

19

Theo kinh nghiệm của tôi, khi sự phức tạp của các mã nghiên cứu khoa học tăng lên, cần phải có một cách tiếp cận rất mô-đun trong lập trình. Điều này có thể gây đau đớn cho các mã với một cơ sở lớn và cổ đại ( f77bất cứ ai?) Nhưng nó là cần thiết để tiến về phía trước. Vì một mô-đun được xây dựng xung quanh một khía cạnh cụ thể của mã (đối với các ứng dụng CFD, hãy nghĩ Điều kiện biên hoặc Nhiệt động lực học), thử nghiệm đơn vị rất có giá trị để xác thực việc triển khai mới và cô lập các vấn đề và phát triển phần mềm hơn nữa.

Các thử nghiệm đơn vị này phải là một cấp dưới xác minh mã (tôi có thể khôi phục giải pháp phân tích phương trình sóng của mình không?) Và 2 cấp dưới xác thực mã (tôi có thể dự đoán các giá trị RMS đỉnh chính xác trong luồng ống hỗn loạn của mình không), chỉ cần đảm bảo rằng lập trình (các đối số được truyền chính xác, là các con trỏ trỏ đến đúng?) và "toán học" (chương trình con này tính hệ số ma sát. Nếu tôi nhập một bộ số và tính toán giải pháp bằng tay, liệu thường trình có mang lại kết quả như nhau không kết quả?) là chính xác. Về cơ bản đi một cấp trên những gì trình biên dịch có thể phát hiện ra, tức là lỗi cú pháp cơ bản.

Tôi chắc chắn sẽ giới thiệu nó cho ít nhất một số mô-đun quan trọng trong ứng dụng của bạn. Tuy nhiên, người ta phải nhận ra rằng nó cực kỳ tẻ nhạt và tốn thời gian vì vậy trừ khi bạn có sức người không giới hạn, tôi sẽ không đề xuất nó cho 100% một mã phức tạp.


Bạn có bất kỳ ví dụ hoặc tiêu chí cụ thể nào để chọn phần nào để kiểm tra đơn vị (và phần nào không) không?
David Ketcheson

@DavidKetcheson Kinh nghiệm của tôi bị giới hạn bởi ứng dụng và ngôn ngữ chúng tôi sử dụng. Vì vậy, đối với mã CFD cho mục đích chung của chúng tôi với khoảng 200 nghìn dòng chủ yếu là F90, chúng tôi đã cố gắng trong một hoặc hai năm qua để thực sự cô lập một số chức năng của mã. Tạo một mô-đun và sử dụng nó trên tất cả các mã không đạt được điều này vì vậy người ta phải thực sự so sánh các mô-đun này và thực tế biến chúng thành các thư viện. Vì vậy, chỉ có rất ít câu lệnh USE và tất cả các kết nối với phần còn lại của mã được thực hiện thông qua các cuộc gọi thông thường. Tất nhiên, các khóa học mà bạn có thể bỏ qua, cũng như phần còn lại của thư viện.
FrenchKainedar

@DavidKetcheson Giống như tôi đã nói trong câu trả lời của mình, điều kiện biên và nhiệt động lực học là hai khía cạnh của mã mà chúng tôi quản lý để thực sự cô lập và không coi trọng những điều này có ý nghĩa. Nói một cách tổng quát hơn, tôi sẽ bắt đầu với một cái gì đó nhỏ và cố gắng làm nó sạch sẽ. Lý tưởng nhất là công việc 2 người. Một người viết các thói quen và tài liệu mô tả giao diện, một người khác nên viết bài kiểm tra đơn vị, lý tưởng nhất là không nhìn vào mã nguồn và chỉ đi theo mô tả giao diện. Bằng cách đó, ý định của thói quen được kiểm tra nhưng tôi nhận ra đây không phải là một điều dễ dàng để tổ chức.
FrenchKainedar

1
Tại sao không bao gồm các loại kiểm thử phần mềm khác (tích hợp, hệ thống) ngoài kiểm thử đơn vị? Bên cạnh thời gian và chi phí, đây sẽ không phải là giải pháp kỹ thuật hoàn chỉnh nhất? Tài liệu tham khảo của tôi là 1 (Phần 3.4.2) và 2 (trang 5). Nói cách khác, Mã nguồn không được kiểm tra bởi các cấp độ kiểm thử phần mềm truyền thống 3 ("Cấp độ kiểm tra")?
ximiki

14

Kiểm tra đơn vị cho các mã khoa học là hữu ích vì nhiều lý do.

Ba cụ thể là:

  • Kiểm tra đơn vị giúp người khác hiểu các ràng buộc của mã của bạn. Về cơ bản, bài kiểm tra đơn vị là một dạng tài liệu.

  • Kiểm tra đơn vị kiểm tra để đảm bảo rằng một đơn vị mã đang trả về kết quả chính xác và kiểm tra để đảm bảo rằng hành vi của chương trình không thay đổi khi các chi tiết được sửa đổi.

  • Sử dụng các bài kiểm tra đơn vị làm cho việc mô đun hóa các mã nghiên cứu của bạn dễ dàng hơn. Điều này có thể đặc biệt quan trọng nếu bạn bắt đầu cố gắng nhắm mục tiêu mã của mình vào một nền tảng mới, ví dụ bạn quan tâm đến việc song song hóa nó hoặc chạy nó trên máy GPGPU.

Hầu hết tất cả, các bài kiểm tra đơn vị cung cấp cho bạn sự tự tin rằng kết quả nghiên cứu bạn đang tạo bằng mã của bạn là hợp lệ và có thể kiểm chứng được.

Tôi lưu ý rằng bạn đề cập đến kiểm tra hồi quy trong câu hỏi của bạn. Trong nhiều trường hợp, kiểm tra hồi quy đạt được thông qua tự động, thực hiện thường xuyên các kiểm tra đơn vị và / hoặc kiểm tra tích hợp (kiểm tra các đoạn mã đó hoạt động chính xác khi kết hợp; trong điện toán khoa học, điều này thường được thực hiện bằng cách so sánh đầu ra với dữ liệu thử nghiệm hoặc kết quả của các chương trình trước đó được tin cậy). Có vẻ như bạn đã sử dụng các bài kiểm tra tích hợp hoặc bài kiểm tra đơn vị ở cấp độ của các thành phần phức tạp lớn thành công.

Điều tôi muốn nói là khi các mã nghiên cứu ngày càng phức tạp và dựa vào mã và thư viện của người khác, điều quan trọng là phải hiểu lỗi xảy ra ở đâu khi xảy ra. Kiểm tra đơn vị cho phép lỗi xác định chính xác dễ dàng hơn nhiều.

Bạn có thể tìm thấy mô tả, bằng chứng và tài liệu tham khảo trong Phần 7 "Lập kế hoạch cho những sai lầm" của bài báo mà tôi là đồng tác giả về Thực tiễn tốt nhất cho Khoa học tính toán hữu ích - nó cũng giới thiệu khái niệm bổ sung về lập trình phòng thủ.


9

Trong lớp deal.II tôi dạy rằng phần mềm mà không có kiểm tra không hoạt động đúng (và tiếp tục căng thẳng mà tôi cố tình nói " không không làm việc một cách chính xác", không phải " có thể không hoạt động chính xác).

Tất nhiên tôi sống theo câu thần chú - đó là cách đối phó.II đã đến để chạy 2.500 bài kiểm tra với mỗi lần cam kết ;-)

Nghiêm trọng hơn, tôi nghĩ Matt đã xác định rõ hai loại bài kiểm tra. Chúng tôi viết các bài kiểm tra đơn vị cho các công cụ cấp thấp hơn và nó sắp xếp một cách tự nhiên để kiểm tra hồi quy cho các công cụ cấp cao hơn. Tôi không nghĩ rằng tôi có thể vẽ một ranh giới rõ ràng có thể tách các thử nghiệm của chúng tôi sang bên này hay bên kia, chắc chắn có nhiều người bước qua đường mà ai đó đã nhìn vào đầu ra và thấy nó phần lớn hợp lý (thử nghiệm đơn vị?) mà không nhìn vào nó đến chút chính xác cuối cùng (kiểm tra hồi quy?).


Tại sao bạn đề xuất hệ thống phân cấp này (đơn vị cho thấp hơn, hồi quy cho cao hơn) so với các mức truyền thống trong kiểm thử phần mềm?
ximiki

@Senseiki - Tôi không có ý đó. Tôi đang nói rằng các bài kiểm tra tồn tại trên một phổ bao gồm tất cả các danh mục được liệt kê trong liên kết của bạn.
Wolfgang Bangerth

8

Có và không. Chắc chắn là không phù hợp với các thói quen cơ bản của bộ công cụ cơ bản mà bạn sử dụng để làm cho cuộc sống của bạn dễ dàng hơn, chẳng hạn như thói quen chuyển đổi, ánh xạ chuỗi, vật lý cơ bản và toán học, v.v. Khi nói đến các lớp hoặc hàm tính toán, chúng thường có thể yêu cầu thời gian chạy dài và bạn thực sự có thể thích kiểm tra chúng như các bài kiểm tra chức năng, thay vì đơn vị. Ngoài ra, không đáng tin cậy và nhấn mạnh rất nhiều các lớp và thực thể có mức độ và cách sử dụng sẽ thay đổi rất nhiều (ví dụ cho mục đích tối ưu hóa) hoặc có chi tiết nội bộ sẽ bị thay đổi vì bất kỳ lý do gì. Ví dụ điển hình nhất là một lớp bao bọc một ma trận khổng lồ, được ánh xạ từ đĩa.


7

Chắc chắn rồi!

Cái gì không đủ cho bạn?

Trong lập trình khoa học hơn bất kỳ loại nào khác, chúng tôi đang phát triển dựa trên việc cố gắng phù hợp với một hệ thống vật lý. Làm thế nào bạn sẽ biết nếu bạn đã làm điều đó ngoài việc kiểm tra? Trước khi bạn bắt đầu viết mã, hãy quyết định cách bạn sẽ sử dụng mã của mình và tìm ra một vài ví dụ chạy. Cố gắng để bắt bất kỳ trường hợp cạnh có thể. Thực hiện theo cách mô-đun - ví dụ, đối với mạng nơ-ron, bạn có thể tạo một bộ các xét nghiệm cho một nơ-ron đơn và một bộ các xét nghiệm cho một mạng nơ-ron hoàn chỉnh. Theo cách đó khi bạn bắt đầu viết mã, bạn có thể đảm bảo nơ ron của bạn hoạt động trước khi bạn bắt đầu làm việc trên mạng. Làm việc trong các giai đoạn như thế này có nghĩa là khi bạn gặp sự cố, bạn chỉ có 'giai đoạn' mã gần đây nhất để kiểm tra, các giai đoạn trước đó đã được kiểm tra.

Ngoài ra, khi bạn có các bài kiểm tra, nếu bạn cần viết lại mã bằng một ngôn ngữ khác (chẳng hạn như chuyển đổi sang CUDA) hoặc ngay cả khi bạn chỉ cập nhật nó, bạn đã có các testcase và bạn có thể sử dụng chúng để thực hiện chắc chắn rằng cả hai phiên bản chương trình của bạn hoạt động theo cùng một cách.


+1: "Làm việc trong các giai đoạn như thế này có nghĩa là khi bạn gặp sự cố, bạn chỉ có 'giai đoạn' mã gần đây nhất để kiểm tra, các giai đoạn trước đó đã được kiểm tra."
ximiki

5

Đúng.

Ý tưởng rằng bất kỳ mã nào được viết mà không có bài kiểm tra đơn vị là anathema. Trừ khi bạn chứng minh mã của mình đúng và sau đó chứng minh bằng chứng chính xác = P.


3
... Và sau đó bạn chứng minh rằng bằng chứng chứng minh là đúng, và ... bây giờ đó là một hố thỏ sâu.
JM

2
Rùa hết đường khiến Dijkstra tự hào!
aterrel

2
Chỉ cần giải quyết trường hợp chung, và sau đó có bằng chứng của bạn chứng minh chính xác! Torus của rùa!
Aesin

5

Tôi sẽ tiếp cận câu hỏi này một cách thực tế hơn là giáo điều. Hãy tự hỏi mình câu hỏi: "Điều gì có thể sai trong chức năng X?" Hãy tưởng tượng điều gì xảy ra với đầu ra khi bạn đưa một số lỗi điển hình vào mã: một tiền tố sai, chỉ mục sai, ... Và sau đó viết các bài kiểm tra đơn vị có khả năng phát hiện loại lỗi đó. Nếu đối với một hàm nhất định, không có cách nào để viết các bài kiểm tra như vậy mà không lặp lại mã của chính hàm đó, thì đừng - nhưng hãy nghĩ về các bài kiểm tra ở cấp độ cao hơn tiếp theo.

Một vấn đề quan trọng hơn nhiều với các bài kiểm tra đơn vị (hoặc trên thực tế là bất kỳ bài kiểm tra nào ) trong mã khoa học là làm thế nào để đối phó với sự không chắc chắn của số học dấu phẩy động. Theo tôi biết không có giải pháp chung tốt nào.


Cho dù bạn kiểm tra thủ công hay tự động bằng cách sử dụng kiểm tra đơn vị, bạn có chính xác các vấn đề tương tự với biểu diễn dấu phẩy động. Tôi rất muốn giới thiệu loạt bài viết xuất sắc của Richard Harris trên tạp chí quá tải của ACCU .
Đánh dấu gian hàng

"Nếu đối với một chức năng nhất định, không có cách nào để viết các bài kiểm tra như vậy mà không lặp lại mã của chính chức năng đó thì không". Bạn có thể xây dựng? Một ví dụ sẽ làm rõ điều này cho tôi.
ximiki

5

Tôi cảm thấy tiếc cho Tangurena - quanh đây, câu thần chú là "Mã chưa được kiểm tra là mã bị hỏng" và điều đó đến từ ông chủ. Thay vì lặp lại tất cả các lý do tốt để thực hiện kiểm tra đơn vị, tôi muốn chỉ thêm một vài chi tiết cụ thể.

  • Sử dụng bộ nhớ nên được kiểm tra. Mọi chức năng phân bổ bộ nhớ nên được kiểm tra để đảm bảo rằng các chức năng lưu trữ và truy xuất dữ liệu vào bộ nhớ đó đang hoạt động đúng. Điều này thậm chí còn quan trọng hơn trong thế giới GPU.
  • Trong khi đề cập ngắn gọn trước đây, thử nghiệm trường hợp cạnh là vô cùng quan trọng. Hãy nghĩ về các bài kiểm tra này giống như cách bạn kiểm tra kết quả của bất kỳ phép tính nào. Đảm bảo mã hoạt động ở các cạnh và thất bại một cách duyên dáng (tuy nhiên bạn xác định điều đó trong mô phỏng của mình) khi các tham số hoặc dữ liệu đầu vào nằm ngoài ranh giới chấp nhận được. Suy nghĩ liên quan đến việc viết loại bài kiểm tra này giúp làm sắc nét công việc của bạn và có thể là một trong những lý do mà bạn hiếm khi tìm thấy ai đó đã viết bài kiểm tra đơn vị nhưng không thấy quy trình này hữu ích.
  • Sử dụng khung kiểm tra (như được đề cập bởi Geoff, người đã cung cấp một liên kết đẹp). Tôi đã sử dụng khung kiểm tra BOOST kết hợp với hệ thống CTest của CMake và có thể đề xuất nó như một cách dễ dàng để viết nhanh các bài kiểm tra đơn vị (cũng như kiểm tra xác nhận và hồi quy).

+1: "Đảm bảo mã hoạt động ở các cạnh và thất bại một cách duyên dáng (tuy nhiên bạn xác định điều đó trong mô phỏng của mình) khi các tham số hoặc dữ liệu đầu vào nằm ngoài ranh giới chấp nhận được."
ximiki

5

Tôi đã sử dụng thử nghiệm đơn vị để có hiệu quả tốt trên một số mã quy mô nhỏ (tức là lập trình viên đơn), bao gồm phiên bản thứ ba của mã phân tích luận án của tôi trong vật lý hạt.

Hai phiên bản đầu tiên đã sụp đổ dưới sức nặng của chính chúng và sự nhân lên của các kết nối.

Những người khác đã viết rằng sự tương tác giữa các mô-đun thường là nơi mã hóa khoa học bị phá vỡ, và họ đúng về điều đó. Nhưng việc chẩn đoán những vấn đề đó sẽ dễ dàng hơn rất nhiều khi bạn có thể chỉ ra một cách thuyết phục rằng mỗi mô-đun đang làm những gì nó có nghĩa là làm.


3

Một cách tiếp cận hơi khác mà tôi đã sử dụng trong khi phát triển bộ giải hóa học (đối với các miền địa chất phức tạp) là cách bạn có thể gọi Kiểm tra đơn vị bằng cách sao chép và dán đoạn trích .

Xây dựng một khai thác thử nghiệm cho mã gốc được nhúng trong một bộ điều biến hệ thống hóa học lớn là không khả thi trong khung thời gian.

Tuy nhiên, tôi đã có thể tạo ra một bộ đoạn mã ngày càng phức tạp cho thấy trình phân tích cú pháp (Boost Spirit) cho các công thức hóa học hoạt động như thế nào, khi kiểm tra đơn vị cho các biểu thức khác nhau.

Thử nghiệm đơn vị cuối cùng, phức tạp nhất rất gần với mã cần thiết trong hệ thống, mà không phải thay đổi mã đó để có thể giả được. Do đó tôi đã có thể sao chép mã đơn vị thử nghiệm của mình.

Điều làm cho nó không chỉ là một bài tập học tập và một bộ hồi quy thực sự là hai yếu tố - các bài kiểm tra đơn vị được giữ trong nguồn chính và chạy như một phần của các bài kiểm tra khác cho ứng dụng đó (và vâng, chúng đã nhận được hiệu ứng phụ từ Boost Tinh thần thay đổi 2 năm sau) - bởi vì mã được sao chép và dán qua đã được sửa đổi tối thiểu trong ứng dụng thực tế, nó có thể có các bình luận tham khảo lại các bài kiểm tra đơn vị để giúp ai đó giữ chúng đồng bộ.


2

Đối với các cơ sở mã lớn hơn, các thử nghiệm (không nhất thiết phải là thử nghiệm đơn vị) cho các công cụ highlevel là hữu ích. Kiểm tra đơn vị cho một số thuật toán đơn giản hơn cũng hữu ích để đảm bảo mã của bạn không hoạt động vô nghĩa vì chức năng trợ giúp của bạn đang sử dụng sinthay vì cos.

Nhưng đối với mã nghiên cứu tổng thể thì rất khó để viết và duy trì các bài kiểm tra. Các thuật toán có xu hướng lớn mà không có kết quả trung gian có ý nghĩa có thể có các bài kiểm tra rõ ràng và thường mất nhiều thời gian để chạy trước khi có kết quả. Tất nhiên bạn có thể kiểm tra đối với các lần chạy tham chiếu có kết quả tốt, nhưng đây không phải là một thử nghiệm tốt trong ý nghĩa kiểm tra đơn vị.

Kết quả thường là xấp xỉ của giải pháp thực sự. Mặc dù bạn có thể kiểm tra các chức năng đơn giản của mình nếu chúng chính xác đến một số epsilon, nhưng sẽ rất khó để xác minh xem một số lưới kết quả có chính xác hay không, được đánh giá bằng cách kiểm tra trực quan bởi người dùng (bạn) trước đó.

Trong những trường hợp như vậy, kiểm tra tự động thường có tỷ lệ chi phí / lợi ích quá cao. Tôi đề nghị một cái gì đó tốt hơn: Viết chương trình thử nghiệm. Ví dụ, tôi đã viết một kịch bản python kích thước trung bình để tạo dữ liệu về kết quả, như biểu đồ kích thước cạnh và góc của lưới, diện tích của tam giác lớn nhất và nhỏ nhất và tỷ lệ của chúng, v.v.

Tôi có thể sử dụng cả hai để đánh giá các lưới đầu vào và đầu ra trong quá trình hoạt động bình thường sử dụng nó để kiểm tra độ tỉnh táo sau khi tôi thay đổi thuật toán. Khi tôi thay đổi thuật toán, tôi không phải lúc nào cũng biết liệu kết quả mới có tốt hơn không, bởi vì thường không có thước đo tuyệt đối nào là xấp xỉ là tốt nhất. Nhưng bằng cách tạo ra các số liệu như vậy tôi có thể nói về một số yếu tố tốt hơn như "Biến thể mới cuối cùng có tỷ lệ góc tốt hơn nhưng tốc độ hội tụ kém hơn".

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.