Những nhược điểm của việc viết mã trước khi viết bài kiểm tra đơn vị là gì?


33

Tôi luôn thấy khuyến nghị rằng trước tiên chúng ta nên viết bài kiểm tra đơn vị và sau đó bắt đầu viết mã. Nhưng tôi cảm thấy rằng đi theo một cách khác thì thoải mái hơn nhiều (đối với tôi) - viết mã và sau đó kiểm tra đơn vị, bởi vì tôi cảm thấy chúng ta đã rõ ràng hơn nhiều sau khi chúng ta đã viết mã thực tế. Nếu tôi viết mã và sau đó là các bài kiểm tra, tôi có thể phải thay đổi mã của mình một chút để làm cho nó có thể kiểm tra được, ngay cả khi tôi tập trung nhiều vào việc tạo ra một thiết kế có thể kiểm tra được. Mặt khác, nếu tôi viết các bài kiểm tra và sau đó là mã, các bài kiểm tra sẽ thay đổi khá thường xuyên và khi mã hình thành.

Như tôi thấy rất nhiều khuyến nghị để bắt đầu viết bài kiểm tra và sau đó chuyển sang viết mã, những nhược điểm nếu tôi làm theo cách khác - viết mã và sau đó là bài kiểm tra đơn vị?


7
+1 để hỏi tại sao một thực hành nhất định là "thực hành tốt nhất" trước khi chấp nhận nó
TaylorOtwell

Câu trả lời:


37

Màu đỏ là câu trả lời. Màu đỏ là những gì bạn nhận được từ chu trình tái cấu trúc đỏ-xanh của TDD mà bạn không thể có được, thử nghiệm lần cuối. Đầu tiên, viết một bài kiểm tra thất bại. Xem nó thất bại. Đó là màu đỏ của bạn, và nó quan trọng. Nó nói: Tôi có yêu cầu này và tôi biết mã của tôi không thỏa mãn nó. Vì vậy, khi bạn chuyển sang bước 2 (màu xanh lá cây), bạn biết, với sự chắc chắn như vậy, mã của bạn hiện đang đáp ứng yêu cầu đó. Bạn biết rằng bạn đã thay đổi cơ sở mã của mình theo cách để đáp ứng yêu cầu.

Các yêu cầu (kiểm tra) được phát triển sau mã, dựa trên mã, làm mất đi sự chắc chắn đó, sự tự tin đó.


+1 - điểm xuất sắc và điểm thực hiện! Cảm ơn ý kiến ​​của bạn!
k25

7
Kiểm tra! = Yêu cầu. Cả hai bài kiểm tra và mã nên được lấy từ các yêu cầu.
Bart van Ingen Schenau

2
@Bart van Ingen Schenau: Sức mạnh của TDD chính xác là các bài kiểm tra là yêu cầu. Hơn nữa, chúng là những yêu cầu thực thi.
mouviciel

1
@Bart: Các bài kiểm tra đơn vị thường quá chi tiết cho các yêu cầu của khách hàng (mức cao), nhưng ý tưởng chắc chắn có, đặc biệt nếu chúng tôi cũng xem xét các bài kiểm tra cấp cao hơn như kiểm tra chấp nhận tự động, một khi được viết, nên là yêu cầu dứt khoát. Đó là bản chất của "thử nghiệm chấp nhận nhanh".
Martin Wickman

3
TDD không phải là về thử nghiệm, nó là về đặc điểm kỹ thuật. Các thử nghiệm được xây dựng theo cách tiếp cận TDD là một phương tiện giao tiếp giữa nhà phát triển và khách hàng để thống nhất về sản phẩm nào nên được thực hiện.
mouviciel

18

Nếu bạn viết mã, và sau đó là các bài kiểm tra, thì rất dễ rơi vào cái bẫy viết các bài kiểm tra để mã đi qua, thay vì viết các bài kiểm tra để đảm bảo mã đáp ứng đặc điểm kỹ thuật.

Điều đó nói rằng, đó chắc chắn không phải là cách duy nhất để làm mọi thứ và không có cách phát triển phần mềm "tốt nhất". Nếu bạn đặt nhiều công việc trả trước vào việc phát triển các trường hợp thử nghiệm, bạn sẽ không biết liệu kiến ​​trúc được đề xuất của bạn có bất kỳ sai sót nào cho đến sau này hay không - trong khi nếu bạn phát triển mã trước, bạn sẽ chạy vào chúng sớm hơn và có thể thiết kế lại với ít chìm hơn cố gắng.


Vâng, bạn đúng về điểm đầu tiên, nhưng tôi luôn đảm bảo rằng tôi không làm điều đó. Nếu thử nghiệm thất bại, tôi luôn đi đến mã và chắc chắn rằng nó đúng và sau đó xem thử nghiệm của tôi có đúng không, và sau đó sửa đổi cái nào sai. Cảm ơn ý kiến ​​của bạn, tôi sẽ giữ điều này trong tâm trí của tôi .. +1 từ tôi cho điểm đầu tiên và điểm cuối cùng ...
k25

2
Nhưng nếu bài kiểm tra vượt qua thì sao? Bài kiểm tra có thể vượt qua vì nó không thực sự thực hiện quy tắc quan tâm; điều đó thực sự không thể xảy ra theo TDD, vì thử nghiệm được cho là thất bại ban đầu và thực hiện - nếu không, bạn không tiếp tục bước 2 cho đến khi bạn sửa nó. Vì vậy, có một chế độ thất bại trong thử nghiệm cuối cùng không tồn tại trong thử nghiệm trước.
Carl Manaster

@Carl Manaster - Vâng, bạn có một điểm hợp lệ. Sau khi tôi viết mã, tôi hoàn toàn nhận thức được các yêu cầu và vì vậy trường hợp kiểm thử đơn vị của tôi sẽ đúng (lý tưởng). Nếu trường hợp thử nghiệm của tôi vượt qua, tôi sẽ nói mã là đúng, nếu thử nghiệm thất bại, tôi sẽ làm theo những gì tôi nói. Nhưng, tôi 100% đồng ý rằng bạn có một điểm hợp lệ ở đó.
k25

@ k25: Vấn đề là nếu trường hợp thử nghiệm của bạn vượt qua, bạn vẫn không biết liệu mã có đúng hay không. Các trường hợp thử nghiệm có thể sai.
Anon.

@Anon. - vâng bạn đúng, tôi cũng sẽ đưa trường hợp này vào tài khoản.
k25

12

Trên thực tế, mọi người bị treo lên trên TDD là về thử nghiệm, mặc dù họ quên hai chữ cái còn lại trong từ viết tắt. Một cái gì đó có thể được đọc ở đây: TDD mà không có T hoặc TDD không phải là về Kiểm tra .

Vấn đề là tôi đã học được rất nhiều thứ khác được đan chặt với TDD. Sẽ không thành vấn đề nếu bạn kiểm tra trước: Vấn đề là suy nghĩ về thiết kế phần mềm .

Để thậm chí có thể viết các bài kiểm tra đơn vị "đúng cách", nghĩa là chúng được cách ly, nhanh chóng và tự động, hy vọng bạn sẽ nhận thấy rằng cần phải suy nghĩ lại về cách sắp xếp mã theo cách mà mã của bạn trở nên dễ dàng hơn để kiểm tra.

Cá nhân tôi đã tự học các nguyên tắc RẮN mà không biết có một thứ như vậy được viết. Điều này là do các bài kiểm tra đơn vị bằng văn bản buộc tôi phải viết lại các lớp để chúng không trở nên quá phức tạp để kiểm tra. Nó dẫn đến những thứ như:

  • Tôi đã phải di chuyển chức năng, rằng nó không có ý nghĩa hoặc nằm trong các phương thức riêng tư, qua các lớp riêng biệt để tôi có thể kiểm tra chúng một cách riêng biệt. (Nguyên tắc trách nhiệm duy nhất).
  • Tôi đã phải tránh các cấu trúc thừa kế lớn và mở rộng triển khai với thành phần thay thế (nổi bật trong nguyên tắc Đóng-Đóng).
  • Tôi phải thông minh về sự kế thừa, tôi đã sử dụng các lớp trừu tượng bất cứ khi nào tôi thấy mã chung có thể được chia sẻ và sử dụng các phương thức sơ khai (Nguyên tắc thay thế Liskov).
  • Tôi đã phải viết các giao diện và các lớp trừu tượng để tôi có thể kiểm tra các lớp trong tách biệt. Mà vô tình dẫn bạn viết đối tượng giả. (Nguyên tắc phân chia giao diện)
  • Bởi vì tôi đã viết rất nhiều giao diện và các lớp trừu tượng, tôi bắt đầu khai báo các biến và tham số để sử dụng loại phổ biến (Nguyên tắc đảo ngược phụ thuộc).

Mặc dù tôi không làm thử nghiệm mọi lúc, tôi vẫn tình cờ tuân theo các nguyên tắc và thực hành OO tốt mà bạn bắt đầu tuân theo, chỉ để thử nghiệm dễ dàng hơn một chút. Bây giờ tôi không viết mã cho riêng mình. Tôi đã viết mã để nó có thể dễ dàng được kiểm tra hoặc quan trọng hơn; Dễ dàng bảo trì .


1
+1 cho RẮN tự nhiên xảy ra với bạn, khi bạn nghĩ về thiết kế phần mềm.
ocodo

+1 (thực ra tôi muốn cho +10 nhưng tôi không thể). Chính xác là suy nghĩ của tôi - bạn liệt kê các điểm cực kỳ tốt. Đó là một lý do tôi hỏi câu hỏi này. Tôi cảm thấy các lớp học trở nên nhiều hơn khi tôi bắt đầu viết bài kiểm tra đơn vị sau khi tôi viết mã. Nhưng tôi muốn thấy những lợi thế / bất lợi của cả hai bên, cảm ơn ý kiến ​​của bạn!
k25

10

Tất cả các câu trả lời khác đều tốt, nhưng có một điểm không được chạm vào. Nếu bạn viết bài kiểm tra trước, nó đảm bảo rằng bài kiểm tra được viết. Thật hấp dẫn, một khi bạn đã viết mã làm việc, bỏ qua các bài kiểm tra và chỉ cần xác minh nó thông qua giao diện người dùng. Nếu bạn có kỷ luật để luôn có một bài kiểm tra thất bại trước khi bạn viết mã, bạn có thể tránh bẫy này.


4

Nếu bạn viết bài kiểm tra của mình trước, nó sẽ cho bạn một cơ hội khác để suy nghĩ về thiết kế của bạn, trước khi thiết kế đó được "đúc bằng đá".

Ví dụ, bạn có thể nghĩ rằng bạn cần một phương thức lấy một bộ tham số nhất định. Và nếu bạn đã viết mã trước, bạn sẽ viết nó theo cách đó và làm cho bài kiểm tra phù hợp với các tham số đã chỉ định. Nhưng nếu bạn viết bài kiểm tra trước, bạn có thể nghĩ "đợi một chút, tôi sẽ không muốn sử dụng tham số này trong mã chính tuyến, vì vậy có lẽ tôi nên thay đổi API."


+1 cho điểm đầu tiên. Nhưng, không đạt được mức tham số, điều gì sẽ xảy ra nếu thiết kế được thảo luận với người khác và được chấp nhận?
k25

@ k25 - nếu một cái gì đó khó sử dụng như thiết kế, nó cần nhiều suy nghĩ hơn. Đôi khi - rất hiếm khi - đó chỉ là một nhiệm vụ khó khăn. Nhưng thường xuyên hơn, nó có thể được giảm xuống các nhiệm vụ đơn giản hơn. Tôi không có liên kết, nhưng cả Gosling hay Goetz đã thực hiện một cuộc phỏng vấn về thiết kế API vài năm trước ... đáng để Google.
Anon

chắc chắn, cảm ơn vì những gợi ý, tôi chắc chắn sẽ xem xét chúng ...
k25

2

Như tôi thấy rất nhiều khuyến nghị để bắt đầu viết bài kiểm tra và sau đó chuyển sang viết mã,

Có một lý do thực sự tốt cho việc này.

Nếu bạn nói "làm những gì cảm thấy đúng", mọi người sẽ làm những điều ngu ngốc nhất, điên rồ nhất.

Nếu bạn nói "viết bài kiểm tra trước", ít nhất mọi người có thể cố gắng làm đúng.

Những nhược điểm nếu tôi làm theo cách khác - viết mã và sau đó kiểm tra đơn vị là gì?

Thông thường, một bài kiểm tra tệ hại và một thiết kế phải được làm lại để có thể kiểm tra được.

Tuy nhiên, đó chỉ là "thông thường". Một số người phát triển các thiết kế và thử nghiệm song song. Một số người đặt mã có thể kiểm tra tại chỗ và viết các bài kiểm tra mà không cần làm lại.

Quy tắc "Thử nghiệm đầu tiên" đặc biệt ở đó để dạy và hướng dẫn những người không có đầu mối nào cả.

Theo cách tương tự, chúng tôi được khuyên luôn luôn nhìn "cả hai cách" trước khi băng qua đường. Tuy nhiên, chúng tôi thực sự không. Và nó không thành vấn đề. Tôi sống ở một quốc gia lái xe bên phải và tôi chỉ cần nhìn sang trái khi bắt đầu băng qua.

Khi tôi đến thăm một quốc gia lái xe bên trái, chỉ nhìn trái có thể khiến tôi thiệt mạng.

Các quy tắc được nêu rất mạnh mẽ cho một lý do.

Những gì bạn làm là vấn đề của riêng bạn.


2

điểm của bài kiểm tra đầu tiên là nó làm bạn suy nghĩ về

  • làm thế nào để kiểm tra mã
  • giao diện mã phải xuất hiện để có thể kiểm tra

nếu bạn đang làm một cái gì đó đơn giản, có lẽ không quan trọng bạn viết cái nào trước (mặc dù việc rèn luyện thói quen thử nghiệm đầu tiên là rất tốt) vì thử nghiệm sẽ đơn giản và giao diện sẽ rõ ràng

nhưng TDD mở rộng thành các thử nghiệm chấp nhận, không chỉ các thử nghiệm đơn vị, và sau đó giao diện trở nên không tầm thường.


1

Trước hết, nếu bạn không viết bài kiểm tra của mình trước thì bạn sẽ không thực hiện Phát triển hướng thử nghiệm (TDD). Lợi ích rất nhiều và thường khó tin cho đến khi bạn thực hành nó nhiều lần. Dưới đây là những lợi ích mà tôi đã nhận được khi làm TDD so với phát triển truyền thống:

  1. Mạng lưới kiểm tra an toàn - cho phép bạn thực hiện các thay đổi lớn mà không sợ phá vỡ thứ gì đó vô tình
  2. Thiết kế hữu cơ - thiết kế mà tôi kết thúc thường khác với thiết kế mà tôi đã làm từ đầu và nó luôn luôn tốt hơn
  3. Năng suất - làm việc hướng tới các mục tiêu nhỏ (vượt qua bài kiểm tra này) và làm cho nó (tất cả các bài kiểm tra vượt qua) hoạt động thực sự tốt cho tôi và giúp tôi có động lực. Thêm vào một cặp và năng suất của tôi đạt đến mức cao mới.

Sách: Beck, K. Phát triển dựa trên thử nghiệm bằng ví dụ

Ví dụ hay: http://jamesshore.com/Blog/Lets-Play/


+1 - điểm hay (đặc biệt là điểm 1) và cảm ơn vì các liên kết!
k25

0

Khi bạn viết một bài kiểm tra, làm thế nào để bạn biết nó sẽ phát hiện ra một tình trạng thất bại? Câu trả lời là "kiểm tra bài kiểm tra". Cách bạn làm điều đó là viết bài kiểm tra trước, xem nó thất bại và chỉ thấy nó vượt qua khi đơn vị được kiểm tra đã được mã hóa thành công (chu trình đỏ / xanh / tái cấu trúc được đề cập trong một trong các câu trả lời khác).

Viết mã trước và sau đó kiểm tra để ngỏ câu hỏi liệu thử nghiệm có cho thấy sự thất bại trung thực không.

Hãy nhớ rằng các bài kiểm tra của bạn thể hiện đặc điểm kỹ thuật. Nếu bạn phải sửa đổi các bài kiểm tra của mình khi mã của bạn "hình thành", điều đó cho thấy rằng các thông số kỹ thuật của bạn đang thay đổi. Đó có thể hoặc không thể là một điều tốt. Nó có thể có nghĩa là sự hiểu biết của bạn về vấn đề ban đầu không đúng. Mặt khác, điều đó có thể có nghĩa là bạn đang thử nghiệm "cách thức" thiết bị đang thực hiện công việc của mình chứ không phải là những gì nó được cho là sẽ hoàn thành.

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.