Mới làm bài kiểm tra đơn vị, làm thế nào để viết bài kiểm tra tuyệt vời? [đóng cửa]


267

Tôi còn khá mới mẻ với thế giới thử nghiệm đơn vị và tôi vừa quyết định thêm phạm vi kiểm tra cho ứng dụng hiện tại của mình trong tuần này.

Đây là một nhiệm vụ rất lớn, chủ yếu là vì số lượng lớp để kiểm tra nhưng cũng vì bài kiểm tra viết hoàn toàn mới đối với tôi.

Tôi đã viết bài kiểm tra cho một loạt các lớp học, nhưng bây giờ tôi tự hỏi liệu tôi có làm đúng không.

Khi tôi viết bài kiểm tra cho một phương thức, tôi có cảm giác viết lại lần thứ hai những gì tôi đã viết trong chính phương thức đó.
Các thử nghiệm của tôi dường như bị ràng buộc chặt chẽ với phương thức (thử nghiệm tất cả các loại tiền mã hóa, hy vọng một số phương thức bên trong được gọi là một số lần, với các đối số nhất định), rằng dường như nếu tôi đã cấu trúc lại phương thức, các thử nghiệm sẽ thất bại ngay cả khi hành vi cuối cùng của phương pháp không thay đổi.

Đây chỉ là một cảm giác, và như đã nói trước đó, tôi không có kinh nghiệm về thử nghiệm. Nếu một số người thử nghiệm có kinh nghiệm hơn ngoài kia có thể cho tôi lời khuyên về cách viết các bài kiểm tra tuyệt vời cho một ứng dụng hiện có, điều đó sẽ được đánh giá rất cao.

Chỉnh sửa: Tôi rất muốn cảm ơn Stack Overflow, tôi đã có những đầu vào tuyệt vời trong chưa đầy 15 phút đã trả lời nhiều hơn số giờ đọc trực tuyến tôi vừa làm.


1
Đây là cuốn sách tốt nhất để thử nghiệm đơn vị: manning.com/osherove Nó giải thích tất cả các thực tiễn tốt nhất, làm và không dành cho thử nghiệm đơn vị.
Ervi B

Một điều tất cả những câu trả lời bỏ qua là kiểm tra đơn vị giống như tài liệu. Ergo, nếu bạn viết một hàm, bạn sẽ ghi lại ý định của nó, bằng cách mô tả các đầu vào và đầu ra của nó (và, có thể, các tác dụng phụ). Một bài kiểm tra đơn vị có nghĩa là để xác minh điều này, sau đó. Và nếu sau đó bạn (hoặc ai đó) thực hiện thay đổi mã, các tài liệu sẽ giải thích ranh giới của những thay đổi có thể được thực hiện và kiểm tra đơn vị đảm bảo các ranh giới được giữ.
Thomas Tempelmann

Câu trả lời:


187

Các thử nghiệm của tôi dường như bị ràng buộc chặt chẽ với phương thức (thử nghiệm tất cả các loại tiền mã hóa, hy vọng một số phương thức bên trong sẽ được gọi là một số lần, với các đối số nhất định), rằng dường như nếu tôi đã cấu trúc lại phương thức, các thử nghiệm sẽ thất bại ngay cả khi hành vi cuối cùng của phương pháp không thay đổi.

Tôi nghĩ rằng bạn đang làm sai.

Một bài kiểm tra đơn vị nên:

  • kiểm tra một phương pháp
  • cung cấp một số đối số cụ thể cho phương thức đó
  • kiểm tra kết quả như mong đợi

Nó không nên nhìn vào bên trong phương thức để xem nó đang làm gì, vì vậy việc thay đổi phần bên trong không nên làm cho bài kiểm tra thất bại. Bạn không nên trực tiếp kiểm tra rằng các phương thức riêng tư đang được gọi. Nếu bạn quan tâm đến việc tìm hiểu xem mã riêng của bạn đang được thử nghiệm hay chưa, hãy sử dụng công cụ bao phủ mã. Nhưng đừng bị ám ảnh bởi điều này: bảo hiểm 100% không phải là một yêu cầu.

Nếu phương thức của bạn gọi các phương thức công khai trong các lớp khác và các cuộc gọi này được đảm bảo bởi giao diện của bạn, thì bạn có thể kiểm tra xem các cuộc gọi này có được thực hiện bằng cách sử dụng khung mô phỏng không.

Bạn không nên sử dụng chính phương thức (hoặc bất kỳ mã nội bộ nào mà nó sử dụng) để tạo ra kết quả mong đợi một cách linh hoạt. Kết quả dự kiến ​​sẽ được mã hóa cứng vào trường hợp thử nghiệm của bạn để nó không thay đổi khi thực hiện thay đổi. Đây là một ví dụ đơn giản về những gì một bài kiểm tra đơn vị nên làm:

testAdd()
{
    int x = 5;
    int y = -2;
    int expectedResult = 3;
    Calculator calculator = new Calculator();
    int actualResult = calculator.Add(x, y);
    Assert.AreEqual(expectedResult, actualResult);
}

Lưu ý rằng cách tính kết quả không được kiểm tra - chỉ có kết quả là đúng. Tiếp tục thêm nhiều trường hợp kiểm tra đơn giản hơn như trên cho đến khi bạn đã bao quát càng nhiều kịch bản càng tốt. Sử dụng công cụ bao phủ mã của bạn để xem nếu bạn đã bỏ lỡ bất kỳ đường dẫn thú vị nào.


13
Cảm ơn rất nhiều, câu trả lời của bạn đã đầy đủ hơn. Bây giờ tôi hiểu rõ hơn những đối tượng giả thực sự là để làm gì: Tôi không cần phải xác nhận mọi cuộc gọi đến các phương thức khác, chỉ những phương thức có liên quan. Tôi cũng không cần biết LÀM THẾ NÀO, nhưng họ làm đúng.
pixelastic

2
Tôi trân trọng nghĩ rằng bạn đang làm sai. Kiểm thử đơn vị là về luồng thực thi mã (kiểm thử hộp trắng). Kiểm thử hộp đen (những gì bạn đang đề xuất) thường là kỹ thuật được sử dụng trong kiểm tra chức năng (kiểm tra tích hợp và hệ thống).
Wes

1
"Một bài kiểm tra đơn vị nên kiểm tra một phương pháp" Tôi thực sự không đồng ý. Một bài kiểm tra đơn vị nên kiểm tra một khái niệm hợp lý. Mặc dù điều đó thường được biểu diễn dưới dạng một phương thức, nhưng điều đó không phải luôn luôn như vậy
robertmain

35

Đối với thử nghiệm đơn vị, tôi thấy cả Test Driven (thử nghiệm đầu tiên, mã thứ hai) và mã đầu tiên, thử nghiệm thứ hai là cực kỳ hữu ích.

Thay vì viết mã, sau đó viết bài kiểm tra. Viết mã sau đó nhìn vào những gì bạn NGHINK mã nên làm. Hãy suy nghĩ về tất cả các mục đích sử dụng của nó và sau đó viết một bài kiểm tra cho mỗi. Tôi thấy các bài kiểm tra viết sẽ nhanh hơn nhưng liên quan nhiều hơn bản thân mã hóa. Các xét nghiệm nên kiểm tra ý định. Cũng suy nghĩ về ý định bạn kết thúc việc tìm kiếm các trường hợp góc trong giai đoạn viết bài kiểm tra. Và tất nhiên, trong khi viết bài kiểm tra, bạn có thể thấy một trong số ít sử dụng gây ra lỗi (một điều tôi thường tìm thấy và tôi rất vui vì lỗi này không làm hỏng dữ liệu và không được kiểm tra).

Tuy nhiên, thử nghiệm gần giống như mã hóa hai lần. Trong thực tế, tôi đã có các ứng dụng có nhiều mã kiểm tra (số lượng) hơn mã ứng dụng. Một ví dụ là một máy trạng thái rất phức tạp. Tôi phải đảm bảo rằng sau khi thêm logic hơn vào nó, toàn bộ mọi thứ luôn hoạt động trên tất cả các trường hợp sử dụng trước đó. Và vì những trường hợp đó khá khó theo dõi bằng cách xem mã, tôi đã có một bộ kiểm tra tốt như vậy cho máy này và tôi tự tin rằng nó sẽ không bị hỏng ngay cả khi thực hiện thay đổi và các thử nghiệm đã lưu lại mông tôi vài lần . Và khi người dùng hoặc người kiểm tra đang tìm thấy lỗi với các trường hợp luồng hoặc góc không được tính toán, hãy đoán xem, thêm vào kiểm tra và không bao giờ xảy ra nữa. Điều này thực sự mang lại cho người dùng sự tự tin trong công việc của tôi ngoài việc làm cho toàn bộ điều này trở nên siêu ổn định. Và khi nó phải được viết lại vì lý do hiệu suất, hãy đoán xem,

Tất cả các ví dụ đơn giản như function square(number)là tuyệt vời và tất cả, và có lẽ là ứng cử viên tồi để dành nhiều thời gian thử nghiệm. Những người làm logic kinh doanh quan trọng, đó là nơi thử nghiệm là quan trọng. Kiểm tra các yêu cầu. Đừng chỉ kiểm tra hệ thống ống nước. Nếu các yêu cầu thay đổi sau đó đoán những gì, các bài kiểm tra cũng phải.

Kiểm tra không nên được kiểm tra theo nghĩa đen mà chức năng foo đã gọi thanh chức năng 3 lần. Điều đó là sai. Kiểm tra xem kết quả và tác dụng phụ có đúng không, không phải là cơ học bên trong.


2
Câu trả lời hay, đã cho tôi niềm tin rằng viết bài kiểm tra sau mã vẫn có thể hữu ích và có thể.
pixelastic

2
Một ví dụ hoàn hảo gần đây. Tôi đã có một chức năng rất đơn giản. Vượt qua nó đúng, nó làm một điều, sai nó làm một điều khác. RẤT ĐƠN GIẢN. Có 4 bài kiểm tra để đảm bảo chức năng thực hiện những gì nó dự định làm. Tôi thay đổi hành vi một chút. Chạy thử nghiệm, POW một vấn đề. Điều buồn cười là khi sử dụng ứng dụng, vấn đề không biểu hiện, nó chỉ xảy ra trong một trường hợp phức tạp. Các trường hợp thử nghiệm đã tìm thấy nó và tôi đã tiết kiệm cho mình hàng giờ đau đầu.
Dmitriy Likhten

"Các bài kiểm tra nên kiểm tra ý định." Điều này tôi nghĩ rằng tổng hợp nó, rằng bạn nên xem qua các mục đích sử dụng của mã và đảm bảo rằng mã có thể chứa chúng. Nó cũng chỉ ra phạm vi của thử nghiệm thực sự nên thử nghiệm và ý tưởng rằng, khi bạn thực hiện thay đổi mã, hiện tại bạn có thể không xem xét đến việc thay đổi đó ảnh hưởng đến tất cả các cách sử dụng mã được quy định - thử nghiệm bảo vệ chống lại sự thay đổi không thỏa mãn tất cả các trường hợp sử dụng dự định.
Greenstick

18

Nó đáng chú ý là các bài kiểm tra đơn vị retro-lắp vào mã hiện là xa khó hơn lái xe việc tạo ra các mã mà với các bài kiểm tra ở nơi đầu tiên. Đó là một trong những câu hỏi lớn trong việc xử lý các ứng dụng cũ ... làm thế nào để kiểm tra đơn vị? Điều này đã được hỏi nhiều lần trước đây (vì vậy bạn có thể bị đóng dưới dạng câu hỏi lừa đảo) và mọi người thường kết thúc ở đây:

Di chuyển mã hiện có để Kiểm tra phát triển theo hướng

Tôi thứ hai đề nghị cuốn sách của câu trả lời được chấp nhận, nhưng ngoài ra có nhiều thông tin hơn được liên kết trong các câu trả lời ở đó.


3
Nếu bạn viết bài kiểm tra đầu tiên hoặc thứ hai, cả hai đều tốt, nhưng khi viết bài kiểm tra, bạn đảm bảo mã của mình có thể kiểm tra được để bạn CÓ THỂ viết bài kiểm tra. Bạn cuộn lên suy nghĩ "làm thế nào tôi có thể kiểm tra điều này" thường là chính nó làm cho mã tốt hơn được viết. Trang bị thêm các trường hợp kiểm tra luôn là một không lớn. Rất vất vả. Đây không phải là vấn đề thời gian, vấn đề về số lượng và khả năng kiểm tra. Tôi không thể đến gặp sếp của mình ngay bây giờ và nói rằng tôi muốn viết các trường hợp thử nghiệm cho hơn một nghìn bảng và việc sử dụng của chúng tôi, quá nhiều bây giờ, sẽ khiến tôi mất một năm và một số logic / quyết định bị lãng quên. Vì vậy, đừng đặt nó quá lâu: P
Dmitriy Likhten

2
Có lẽ câu trả lời được chấp nhận đã thay đổi. Có một câu trả lời từ Linx khuyến nghị Nghệ thuật thử nghiệm đơn vị của Roy Osherove, manning.com/osherove
thelem

15

Đừng viết bài kiểm tra để có được phạm vi bảo hiểm đầy đủ của mã của bạn. Viết bài kiểm tra đảm bảo yêu cầu của bạn. Bạn có thể khám phá ra những mật mã không cần thiết. Ngược lại, nếu cần thiết, họ ở đó để thực hiện một số loại yêu cầu; tìm nó là gì và kiểm tra yêu cầu (không phải đường dẫn).

Giữ các bài kiểm tra của bạn nhỏ: một bài kiểm tra cho mỗi yêu cầu.

Sau này, khi bạn cần thay đổi (hoặc viết mã mới), hãy thử viết một bài kiểm tra trước. Chỉ một. Sau đó, bạn sẽ thực hiện bước đầu tiên trong phát triển dựa trên thử nghiệm.


Cảm ơn, thật hợp lý khi chỉ có các bài kiểm tra nhỏ cho yêu cầu nhỏ, từng bài một. Bài học kinh nghiệm.
pixelastic

13

Kiểm thử đơn vị là về đầu ra bạn nhận được từ một hàm / phương thức / ứng dụng. Nó không quan trọng ở tất cả các kết quả được tạo ra như thế nào, nó chỉ quan trọng rằng nó là chính xác. Do đó, cách tiếp cận của bạn trong việc đếm các cuộc gọi đến các phương thức bên trong và như vậy là sai. Những gì tôi có xu hướng làm là ngồi xuống và viết những gì một phương thức sẽ trả về các giá trị đầu vào nhất định hoặc một môi trường nhất định, sau đó viết một bài kiểm tra so sánh giá trị thực được trả về với những gì tôi nghĩ ra.


Cảm ơn ! Tôi có cảm giác mình đã làm sai, nhưng có ai đó thực sự nói với tôi thì tốt hơn.
pixelastic

8

Hãy thử viết một bài kiểm tra đơn vị trước khi viết phương pháp mà nó sẽ kiểm tra.

Điều đó chắc chắn sẽ buộc bạn phải suy nghĩ một chút khác biệt về cách mọi thứ đang được thực hiện. Bạn sẽ không biết làm thế nào phương pháp sẽ hoạt động, chỉ là những gì nó phải làm.

Bạn phải luôn luôn kiểm tra kết quả của phương pháp, chứ không phải làm thế nào phương thức có được những kết quả đó.


Vâng, tôi rất thích có thể làm điều đó, ngoại trừ việc các phương thức đã được viết. Tôi chỉ muốn kiểm tra chúng. Tôi sẽ viết bài kiểm tra trước các phương pháp trong tương lai, tho.
pixelastic

2
@pixelastic giả vờ rằng các phương pháp chưa được viết?
committedandroider

4

các xét nghiệm được cho là để cải thiện khả năng bảo trì. Nếu bạn thay đổi một phương pháp và phá vỡ thử nghiệm có thể là một điều tốt. Mặt khác, nếu bạn xem phương thức của mình là một hộp đen thì không có vấn đề gì bên trong phương thức. Thực tế là bạn cần chế giễu mọi thứ cho một số thử nghiệm, và trong những trường hợp đó, bạn thực sự không thể coi phương pháp này là một hộp đen. Điều duy nhất bạn có thể làm là viết một bài kiểm tra tích hợp - bạn tải lên một bản sao hoàn chỉnh của dịch vụ đang được kiểm tra và để nó làm điều đó giống như nó sẽ chạy trong ứng dụng của bạn. Sau đó, bạn có thể coi nó như một hộp đen.

When I'm writing tests for a method, I have the feeling of rewriting a second time what I          
already wrote in the method itself.
My tests just seems so tightly bound to the method (testing all codepath, expecting some    
inner methods to be called a number of times, with certain arguments), that it seems that
if I ever refactor the method, the tests will fail even if the final behavior of the   
method did not change.

Điều này là do bạn đang viết bài kiểm tra sau khi bạn viết mã. Nếu bạn làm theo cách khác (viết các bài kiểm tra trước) thì nó sẽ không cảm thấy như vậy.


Cảm ơn ví dụ về hộp đen, tôi đã không nghĩ như vậy. Tôi ước tôi đã phát hiện ra thử nghiệm đơn vị sớm hơn, nhưng thật không may, đó không phải là trường hợp và tôi bị mắc kẹt với một ứng dụng để thêm các thử nghiệm. Không có cách nào để thêm các bài kiểm tra vào một dự án hiện có mà không cảm thấy chúng bị hỏng?
pixelastic

1
Viết bài kiểm tra sau khác với bài kiểm tra viết trước, vì vậy bạn bị mắc kẹt với nó. tuy nhiên, những gì bạn có thể làm là thiết lập các bài kiểm tra để chúng thất bại trước, sau đó làm cho chúng vượt qua bằng cách đưa lớp của bạn vào kiểm tra .... làm một cái gì đó như thế, đưa trường hợp của bạn vào thử nghiệm sau khi thử nghiệm ban đầu thất bại. Điều tương tự với giả - ban đầu, giả không có kỳ vọng, và sẽ thất bại vì phương pháp được thử sẽ làm một cái gì đó với giả, sau đó thực hiện kiểm tra vượt qua. Tôi sẽ không ngạc nhiên nếu bạn tìm thấy rất nhiều lỗi theo cách này.
hvgotcodes

Ngoài ra, hãy thực sự cụ thể với mong đợi của bạn. Đừng khẳng định rằng kiểm tra trả về một đối tượng, kiểm tra xem đối tượng đó có các giá trị khác nhau trên đó không. Kiểm tra xem khi một giá trị được coi là null, thì nó là. Bạn cũng có thể phá vỡ nó một chút bằng cách thực hiện một số tái cấu trúc mà bạn muốn làm, sau khi bạn thêm một số thử nghiệm.
hvgotcodes
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.