2010-07-15 28 views
156

Tôi khá mới đối 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 có của mình trong tuần này.Mới để thử nghiệm đơn vị, làm thế nào để viết các bài kiểm tra tuyệt vời?

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

Tôi đã viết các 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 pháp, 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 pháp (kiểm tra tất cả các mã lệnh, mong đợi một số phương thức bên trong được gọi một số lần, với các đối số nhất định), dường như nếu tôi tái cấu trúc phương pháp, nếu hành vi cuối cùng của phương pháp không thay đổi.

Đây chỉ là cảm giác và như đã nói ở trên, tôi không có kinh nghiệm kiểm tra. Nếu một số người thử nghiệm có kinh nghiệm hơn ở đó 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á cao.

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

Trả lời

112

Bài kiểm tra của tôi dường như bị ràng buộc chặt chẽ với phương pháp (kiểm tra tất cả các mã, mong đợi 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), dường như là tôi đã tái cấu trúc phương thức, kiểm tra 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ĩ bạn đang làm sai.

Một thử nghiệm đơn vị nên:

  • thử nghiệm một phương pháp
  • cung cấp một số lập luận cụ thể để phương pháp mà
  • thử nghiệm mà kết quả được như mong đợi

Nó không nên nhìn vào bên trong các phương pháp để xem những gì nó đang làm, vì vậy việc thay đổi bên trong không nên làm cho thử nghiệm thất bại. Bạn không nên trực tiếp kiểm tra 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 có đang được kiểm tra hay không thì hãy sử dụng công cụ bao gồm 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 pháp của bạn gọi 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ó đang được thực hiện hay không bằng cách sử dụng khung làm giả.

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 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 trong trường hợp kiểm tra của bạn để nó không thay đổi khi thay đổi thực hiện. Dưới đây là ví dụ đơn giản về thử nghiệm đơ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 kết quả được tính không được kiểm tra - chỉ kết quả là chính xác. Tiếp tục bổ sung thêm nhiều trường hợp thử nghiệm đơn giản hơn như trên cho đến khi bạn đã bao phủ nhiều kịch bản nhất có thể. Sử dụng công cụ bảo hiểm mã của bạn để xem bạn có bỏ lỡ bất kỳ đường dẫn thú vị nào không.

+7

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

+1

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

14

Cần lưu ý rằng các thử nghiệm đơn vị theo đơn vị retro vào mã hiện tại là đến nay khó khăn hơn so với việc tạo mã đó bằng các thử nghiệm ngay từ đầu. Đó là một trong những câu hỏi lớn trong việc đối phó với 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 (do đó bạn thể được đóng lại như một câu hỏi dupe), và người ta thường kết thúc ở đây:

Moving existing code to Test Driven Development

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

+3

Nếu bạn viết các bài kiểm tra đầu tiên hoặc thứ hai, nó là cả hai tốt, nhưng khi viết các bài kiểm tra bạn đảm bảo mã của bạn có thể kiểm tra để bạn có thể viết các bài kiểm tra. Bạn suy nghĩ về "làm cách nào tôi có thể kiểm tra điều này" thường là chính bản thân nó đã tạo ra mã tốt hơn để viết. Retrofitting trường hợp kiểm tra luôn luôn là một lớn không-không. Rất khó. Nó không phải là một vấn đề thời gian, vấn đề về số lượng và khả năng kiểm thử của nó. Tôi không thể đến gặp ông chủ của tôi 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à sử dụng, quá nhiều giờ, sẽ mất một năm, và một số logic/quyết định bị lãng quên. Vì vậy, không đặt nó quá dài: P –

+1

Có lẽ câu trả lời được chấp nhận đã thay đổi. Có một câu trả lời từ Linx đề xuất Nghệ thuật thử nghiệm đơn vị bởi Roy Osherove, http://www.manning.com/osherove/ – thelem

5

Hãy thử viết Bài kiểm tra đơn vị trước khi viết phương pháp 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 có ý tưởng làm thế nào phương pháp sẽ làm việc, chỉ cần những gì nó là vụ phải làm.

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

+0

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

+1

@pixelastic giả vờ rằng các phương pháp không được viết? – committedandroider

10

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

Giữ thử nghiệm của bạn nhỏ: một thử nghiệm cho mỗi yêu cầu.

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

+0

Xin cảm ơn, chỉ có các bài kiểm tra nhỏ cho các yêu cầu nhỏ, mỗi lần một. Bài học kinh nghiệm. – pixelastic

10

Kiểm tra đơn vị là về đầu ra bạn nhận được từ một hàm/phương pháp/ứng dụng. Nó không quan trọng ở tất cả như thế nào kết quả được sản xuất, nó chỉ là vấn đề mà nó là chính xác. Vì vậy, cách tiếp cận của bạn để đế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 nên trả về một số giá trị đầu vào 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 tế được trả về với những gì tôi đã đưa ra.

+0

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

3

các thử nghiệm được cho là cải thiện khả năng bảo trì. Nếu bạn thay đổi phương thức và ngắt kiểm tra, có thể là một điều tốt. Mặt khác, nếu bạn nhìn vào phương pháp của bạn như một hộp đen hơn là nó không quan trọng những gì là bên trong phương pháp. Thực tế là bạn cần phải thử những thứ cho một số xét 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à một thử nghiệm tích hợp wrte - bạn tải lên một thể hiện đầy đủ của dịch vụ đang được thử nghiệm và có nó làm việc 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 viết mã. Nếu bạn đã làm nó theo cách khác xung quanh (đã viết các bài kiểm tra đầu tiên) nó sẽ không cảm thấy theo cách này.

+0

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

+1

Viết các bài kiểm tra sau khi khác với viết bài kiểm tra trước đây, 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 không thành công trước, sau đó làm cho chúng vượt qua bằng cách đặt lớp của bạn đang được kiểm tra vào .... làm điều gì đó tương tự, đặt trường hợp của bạn được kiểm tra sau khi thử nghiệm ban đầu không thành công . Điều tương tự với mocks - ban đầu giả lập không có kỳ vọng, và sẽ thất bại vì phương pháp được thử nghiệm sẽ làm điều gì đó với mô hình, sau đó thực hiện kiểm tra. Tôi sẽ không ngạc nhiên nếu bạn tìm thấy nhiều lỗi theo cách này. – hvgotcodes

+0

cũng, thực sự cụ thể với mong đợi của bạn. Không khẳng định rằng thử nghiệm trả về một đối tượng, kiểm tra xem đối tượng có các giá trị khác nhau trên nó không. Kiểm tra rằng khi một giá trị được cho là rỗng, 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ố phép 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

3

Đây là cuốn sách tốt nhất để thử nghiệm đơn vị: http://www.manning.com/osherove/

Nó giải thích tất cả các thực hành tốt nhất, làm, và không cho kiểm tra đơn vị.

+0

Cảm ơn bạn, tôi sẽ xem xét điều đó. Tôi đã mua cuốn sách Kent Beck về TDD nhưng tôi nghĩ trước tiên tôi cần kiểm tra trang bìa ứng dụng hiện có của mình trước. – pixelastic

22

Để kiểm tra đơn vị, tôi đã tìm thấy cả Kiểm tra lái xe (kiểm tra trước, mã thứ hai) và mã đầu tiên, kiểm tra thứ hai cực kỳ hữu ích.

Thay vì viết mã, sau đó viết kiểm tra. Viết mã sau đó nhìn vào những gì bạn nghĩ rằng 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 nhanh hơn nhưng có 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. Ngoài ra suy nghĩ về những ý định bạn gió lên tìm trường hợp góc trong giai đoạn viết thử nghiệm. Và tất nhiên trong khi viết thử nghiệm bạn có thể tìm thấy một trong số ít sử dụng gây ra lỗi (điều tôi thường 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 trong đó 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 chắc chắn rằng sau khi thêm nhiều logic hơn vào nó, toàn bộ điều luôn luôn làm việc 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 cho máy này mà tôi tin rằng nó sẽ không bị vỡ ngay cả sau khi thực hiện thay đổi, và các bài kiểm tra đã cứu được một vài lần . Và khi người dùng hoặc người thử nghiệm tìm thấy lỗi với các trường hợp dòng chảy hoặc góc bị mất tích, hãy đoán xem điều gì đã được 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 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, nó hoạt động như thế nào trên tất cả các đầu vào nhờ vào các bài kiểm tra.

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ó thể là ứng cử viên xấu 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 thì hãy đoán xem, các bài kiểm tra cũng phải như thế nào.

Kiểm tra không được kiểm tra nghĩa là hàm 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 cơ chế bên trong.

+1

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

+1

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ó có một điều, sai nó làm khác. RẤT ĐƠN GIẢN. Đã có 4 kiểm tra 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ỉ trong một trường hợp phức tạp mà nó làm. Các trường hợp thử nghiệm tìm thấy nó và tôi đã tiết kiệm cho mình giờ nhức đầu. –

Các vấn đề liên quan