Hành vi kiểm tra đơn vị mà không khớp nối với chi tiết thực hiện


14

Trong bài nói chuyện về TDD, mọi chuyện đã sai ở đâu , Ian Cooper đẩy ý định ban đầu của Kent Beck đằng sau thử nghiệm đơn vị trong TDD (để kiểm tra hành vi, không phải phương pháp của các lớp cụ thể) và lập luận về việc tránh ghép các bài kiểm tra với việc thực hiện.

Trong trường hợp hành vi như save X to some data sourcetrong một hệ thống có một bộ dịch vụ và kho lưu trữ điển hình, làm thế nào chúng ta có thể kiểm tra việc lưu một số dữ liệu ở cấp dịch vụ, thông qua kho lưu trữ, mà không ghép thử nghiệm với các chi tiết triển khai (như gọi một phương thức cụ thể )? Là tránh loại khớp nối này thực sự không có giá trị nỗ lực / xấu theo một cách nào đó?


1
Nếu bạn muốn kiểm tra dữ liệu đó đã được lưu trong kho lưu trữ, thì thử nghiệm sẽ phải thực sự đi và kiểm tra kho lưu trữ để xem liệu dữ liệu có ở đó không, phải không? Hay tôi đang thiếu một cái gì đó?

Câu hỏi của tôi là về việc tránh ghép các bài kiểm tra với một chi tiết triển khai như gọi một phương thức cụ thể trên kho lưu trữ, hoặc thực sự nếu đó là điều nên làm.
Andy Hunt

Câu trả lời:


8

Ví dụ cụ thể của bạn là một trường hợp mà bạn thường phải kiểm tra bằng cách kiểm tra xem một phương thức nào đó đã được gọi chưa, bởi vì saving X to data sourcecó nghĩa là giao tiếp với một phụ thuộc bên ngoài , vì vậy hành vi bạn phải kiểm tra là giao tiếp diễn ra như mong đợi .

Tuy nhiên, đây không phải là một điều xấu. Các giao diện ranh giới giữa ứng dụng của bạn và các phụ thuộc bên ngoài của nó không phảichi tiết triển khai , trên thực tế, chúng được xác định trong kiến ​​trúc của hệ thống của bạn; điều đó có nghĩa là một ranh giới như vậy không có khả năng thay đổi (hoặc nếu nó phải, nó sẽ là loại thay đổi ít thường xuyên nhất). Do đó, việc ghép các bài kiểm tra của bạn với một repositorygiao diện sẽ không gây ra cho bạn quá nhiều rắc rối (nếu có, hãy xem xét nếu giao diện đó không đánh cắp trách nhiệm từ ứng dụng).

Bây giờ, chỉ xem xét các quy tắc kinh doanh của một ứng dụng, tách rời khỏi UI, cơ sở dữ liệu và các dịch vụ bên ngoài khác. Đây là bạn phải được tự do thay đổi cả cấu trúc và hành vi của mã. Đây là nơi kiểm tra khớp nối và chi tiết triển khai sẽ buộc bạn thay đổi nhiều mã kiểm tra hơn mã sản xuất, ngay cả khi không có thay đổi trong hành vi chung của ứng dụng. Đây là nơi thử nghiệm Statethay vì Interactiongiúp chúng tôi đi nhanh hơn.

Tái bút: Tôi không có ý định nói rằng việc kiểm tra bởi Nhà nước hoặc Tương tác là cách thực sự duy nhất của TDD - Tôi tin rằng đó là vấn đề sử dụng đúng công cụ cho đúng công việc.


Khi bạn đề cập đến "giao tiếp với một phụ thuộc bên ngoài", bạn đang đề cập đến các phụ thuộc bên ngoài như là các phụ thuộc bên ngoài cho đơn vị được thử nghiệm, hoặc các bên ngoài toàn bộ hệ thống?
Andy Hunt

"Phụ thuộc bên ngoài" Tôi có nghĩa là bất cứ điều gì bạn có thể được coi là một bổ trợ cho ứng dụng của bạn. Theo ứng dụng, ý tôi là các quy tắc kinh doanh, bất khả tri của bất kỳ loại chi tiết nào, chẳng hạn như sử dụng khuôn khổ nào để duy trì hoặc UI. Tôi nghĩ rằng chú Bob có thể giải thích điều đó tốt hơn, như trong cuộc nói chuyện này: youtube.com/watch?v=WpkDN78P884
MichelHenrich

Tôi nghĩ rằng đây là cách tiếp cận lý tưởng, như bài nói, để kiểm tra trên cơ sở "tính năng" hoặc "hành vi" và một thử nghiệm cho mỗi tính năng hoặc hành vi (hoặc hoán vị của một, tức là các tham số khác nhau). Tuy nhiên, nếu tôi có 1 thử nghiệm "hạnh phúc" cho một tính năng, để thực hiện TDD, điều đó có nghĩa là tôi sẽ có một cam kết khổng lồ (và đánh giá mã) cho tính năng đó, đó là một ý tưởng tồi. Làm thế nào điều này sẽ tránh được? Viết một phần của tính năng đó dưới dạng thử nghiệm và tất cả mã được liên kết với nó, sau đó tăng dần phần còn lại của tính năng trong các lần xác nhận tiếp theo?
jordan

Tôi thực sự muốn thấy một ví dụ thực tế về các bài kiểm tra được kết hợp để thực hiện.
positiveGuy

7

Giải thích của tôi về cuộc nói chuyện đó là:

  • kiểm tra thành phần, không phải lớp học.
  • kiểm tra các thành phần thông qua các cổng giao diện của họ.

Nó không được nêu trong bài nói chuyện, nhưng tôi nghĩ bối cảnh giả định cho lời khuyên là một cái gì đó như:

  • bạn đang phát triển một hệ thống cho người dùng, không phải là thư viện tiện ích hoặc khung.
  • mục tiêu của thử nghiệm là cung cấp thành công càng nhiều càng tốt trong phạm vi ngân sách cạnh tranh.
  • các thành phần được viết bằng một ngôn ngữ đơn, trưởng thành, có thể được nhập tĩnh, như C # / Java.
  • một thành phần có thứ tự 10000-50000 dòng; một dự án Maven hoặc VS, plugin OSGI, v.v.
  • các thành phần được viết bởi một nhà phát triển duy nhất hoặc nhóm tích hợp chặt chẽ.
  • bạn đang theo thuật ngữ và cách tiếp cận của một cái gì đó như kiến trúc lục giác
  • một cổng thành phần là nơi bạn rời khỏi ngôn ngữ địa phương và hệ thống loại của nó, phía sau, chuyển sang http / SQL / XML / byte / ...
  • gói tất cả các cổng là các giao diện được gõ, theo nghĩa Java / C #, có thể có các triển khai được chuyển sang các công nghệ chuyển đổi.

Vì vậy, kiểm tra một thành phần là phạm vi lớn nhất có thể trong đó một cái gì đó vẫn có thể được gọi là kiểm thử đơn vị một cách hợp lý. Điều này khá khác với cách một số người, đặc biệt là các học giả, sử dụng thuật ngữ này. Nó không giống như các ví dụ trong hướng dẫn công cụ kiểm tra đơn vị điển hình. Tuy nhiên, nó phù hợp với nguồn gốc của nó trong thử nghiệm phần cứng; bảng và mô-đun được thử nghiệm đơn vị, không phải dây và ốc vít. Hoặc ít nhất bạn không chế tạo một chiếc Boeing giả để thử vít ...

Ngoại suy từ đó, và đưa vào một số suy nghĩ của riêng tôi,

  • Mọi giao diện sẽ là đầu vào, đầu ra hoặc cộng tác viên (như cơ sở dữ liệu).
  • bạn kiểm tra các giao diện đầu vào; gọi các phương thức, khẳng định các giá trị trả về.
  • bạn chế nhạo các giao diện đầu ra; xác minh các phương thức dự kiến ​​được gọi cho một trường hợp thử nghiệm nhất định.
  • bạn giả mạo các cộng tác viên; cung cấp một triển khai đơn giản nhưng làm việc

Nếu bạn làm điều đó đúng cách và sạch sẽ, bạn hầu như không cần một công cụ chế giễu; nó chỉ được sử dụng một vài lần cho mỗi hệ thống.

Một cơ sở dữ liệu nói chung là một cộng tác viên, vì vậy nó bị làm giả thay vì bị chế giễu. Điều này sẽ gây đau đớn khi thực hiện bằng tay; may mắn là những việc như vậy đã tồn tại .

Mẫu thử nghiệm cơ bản là thực hiện một số chuỗi hoạt động (ví dụ: lưu và tải lại tài liệu); xác nhận nó hoạt động. Điều này giống như đối với bất kỳ kịch bản thử nghiệm nào khác; không có thay đổi triển khai (làm việc) có thể khiến thử nghiệm như vậy thất bại.

Ngoại lệ là nơi các bản ghi cơ sở dữ liệu được viết nhưng không bao giờ được đọc bởi hệ thống đang thử nghiệm; ví dụ: nhật ký kiểm toán hoặc tương tự. Đây là những đầu ra, và do đó nên bị chế giễu. Các mẫu thử nghiệm được thực hiện một số chuỗi các hoạt động; xác nhận giao diện kiểm toán đã được gọi với các phương thức và đối số như được chỉ định.

Lưu ý rằng ngay cả ở đây, với điều kiện bạn đang sử dụng một công cụ giả định an toàn kiểu như mockito , việc đổi tên một phương thức giao diện có thể gây ra lỗi thử nghiệm. Nếu bạn sử dụng IDE với các bài kiểm tra được tải, nó sẽ được cấu trúc lại cùng với đổi tên phương thức. Nếu bạn không làm, bài kiểm tra sẽ không được biên dịch.


Bạn có thể mô tả / cho tôi một ví dụ cụ thể về một cổng giao diện không?
positiveGuy

những gì một ví dụ về giao diện đầu ra. Bạn có thể được cụ thể trong mã? Tương tự với giao diện đầu vào.
Tích cực,

Một giao diện (theo nghĩa Java / C #) bao bọc một cổng, có thể là bất cứ thứ gì nói chuyện với thế giới bên ngoài (d / b, socket, http, ....). Giao diện đầu ra là một phương thức không có phương thức với các giá trị trả về đến từ thế giới bên ngoài thông qua cổng, chỉ có ngoại lệ hoặc tương đương.
soru

Giao diện đầu vào thì ngược lại, cộng tác viên là cả đầu vào và đầu ra.
soru

1
Tôi nghĩ rằng bạn đang nói về một cách tiếp cận thiết kế hoàn toàn khác và tập hợp thuật ngữ với mô tả trong video. Nhưng 90% thời gian một kho lưu trữ (tức là cơ sở dữ liệu) là một cộng tác viên, không phải là đầu vào hoặc đầu ra. Và do đó, giao diện của nó là một giao diện cộng tác.
soru

0

Đề nghị của tôi là sử dụng phương pháp thử nghiệm dựa trên trạng thái:

GIVEN Chúng tôi có DB thử nghiệm ở trạng thái đã biết

KHI Dịch vụ được gọi với đối số X

THEN Khẳng định rằng DB đã thay đổi từ trạng thái ban đầu sang trạng thái mong đợi bằng cách gọi các phương thức lưu trữ chỉ đọc và kiểm tra các giá trị được trả về của chúng

Theo cách đó, bạn không dựa vào bất kỳ thuật toán nội bộ nào của dịch vụ và được tự do cấu trúc lại việc thực hiện dịch vụ mà không phải thay đổi các thử nghiệm.

Khớp nối duy nhất ở đây là cuộc gọi phương thức dịch vụ và các cuộc gọi kho lưu trữ cần thiết để đọc dữ liệu từ DB, điều này là tốt.

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.