Cách đơn vị phương thức kiểm tra trả về tập hợp trong khi tránh logic trong kiểm tra


14

Tôi đang lái thử một phương pháp là tạo ra một tập hợp các đối tượng dữ liệu. Tôi muốn xác minh rằng các thuộc tính của các đối tượng đang được đặt chính xác. Một số thuộc tính sẽ được đặt thành cùng một thứ; những cái khác sẽ được đặt thành một giá trị phụ thuộc vào vị trí của chúng trong bộ sưu tập. Cách tự nhiên để làm điều này dường như là với một vòng lặp. Tuy nhiên, Roy Osherove khuyến cáo không nên sử dụng logic trong các bài kiểm tra đơn vị ( Nghệ thuật kiểm tra đơn vị , 178). Anh ta nói:

Một bài kiểm tra có chứa logic thường là kiểm tra nhiều thứ cùng một lúc, điều này không được khuyến khích, vì bài kiểm tra ít dễ đọc hơn và dễ vỡ hơn. Nhưng logic kiểm tra cũng thêm sự phức tạp có thể chứa một lỗi ẩn.

Các thử nghiệm, theo nguyên tắc chung, là một chuỗi các cuộc gọi phương thức không có luồng điều khiển, thậm chí không try-catchvà với các cuộc gọi khẳng định.

Tuy nhiên, tôi không thể thấy bất cứ điều gì sai với thiết kế của mình (làm thế nào khác để bạn tạo một danh sách các đối tượng dữ liệu, một số giá trị của chúng phụ thuộc vào vị trí của chúng trong chuỗi? Có cái gì đó không thân thiện với thiết kế của tôi không? Hay là quá cứng nhắc dành cho việc giảng dạy của Osherove? Hoặc có một số phép thuật thử nghiệm đơn vị bí mật mà tôi không biết về việc giải quyết vấn đề này? (Tôi đang viết bằng C # / VS2010 / NUnit, nhưng tìm kiếm câu trả lời không biết ngôn ngữ nếu có thể.)


4
Tôi khuyên bạn không nên lặp. Nếu bài kiểm tra của bạn là điều thứ ba có Thanh được đặt thành Frob, thì hãy viết một bài kiểm tra để kiểm tra cụ thể rằng Thanh thứ ba có phải là Frob không. Đó là một thử nghiệm của chính nó, đi thẳng vào nó, không có vòng lặp. Nếu bài kiểm tra của bạn là bạn nhận được một bộ sưu tập gồm 5 điều, thì đó cũng là một bài kiểm tra. Điều đó không có nghĩa là bạn không bao giờ có một vòng lặp (rõ ràng hay nói cách khác), chỉ là bạn không cần phải thường xuyên . Ngoài ra, coi cuốn sách của Osherove là nhiều hướng dẫn hơn các quy tắc thực tế.
Anthony Pegram

1
@AnthonyPegram Bộ không có thứ tự - Frob đôi khi có thể là thứ 3, đôi khi có thể là thứ 2. Bạn không thể dựa vào nó, tạo một vòng lặp (hoặc tính năng ngôn ngữ như Python in), nếu thử nghiệm là "Frob đã được thêm thành công vào bộ sưu tập hiện có".
Izkata

1
@Izbata, câu hỏi của anh đặc biệt đề cập rằng đặt hàng là quan trọng. Ông nói: "những người khác sẽ được đặt thành một giá trị phụ thuộc vào vị trí của họ trong bộ sưu tập." Có rất nhiều loại bộ sưu tập trong C # (ngôn ngữ mà anh ấy tham khảo) được chèn theo thứ tự. Đối với vấn đề đó, bạn cũng có thể dựa vào thứ tự với các danh sách trong Python, một ngôn ngữ bạn đề cập.
Anthony Pegram

Ngoài ra, giả sử bạn đang thử nghiệm phương pháp Đặt lại trên bộ sưu tập. Bạn cần lặp qua bộ sưu tập và kiểm tra từng mục. Tùy thuộc vào kích thước của bộ sưu tập, không kiểm tra nó trong một vòng lặp là vô lý. Hoặc giả sử tôi đang thử nghiệm thứ gì đó được cho là tăng từng mục trong bộ sưu tập. Bạn có thể đặt tất cả các mục thành cùng một giá trị, gọi số gia của bạn, sau đó kiểm tra. Bài kiểm tra đó thật tệ. Bạn nên đặt một vài trong số chúng thành các giá trị khác nhau, tăng số gọi và kiểm tra xem tất cả các giá trị khác nhau có tăng đúng không. Kiểm tra chỉ một mục ngẫu nhiên trong bộ sưu tập là rất nhiều cơ hội.
iheanyi

Tôi sẽ không trả lời theo cách này vì tôi sẽ nhận được rất nhiều lượt tải xuống, nhưng tôi thường chỉ toString()là Bộ sưu tập và so sánh với những gì nó nên có. Đơn giản và hiệu quả.
dùng949300

Câu trả lời:


16

TL; DR:

  • Viết bài kiểm tra
  • Nếu kiểm tra làm quá nhiều, mã cũng có thể làm quá nhiều.
  • Nó có thể không phải là một bài kiểm tra đơn vị (nhưng không phải là một bài kiểm tra tồi).

Điều đầu tiên để thử nghiệm là về giáo điều là không có ích. Tôi thích đọc The Way of Testivus , trong đó chỉ ra một số vấn đề với giáo điều một cách nhẹ nhàng.

Viết bài kiểm tra cần viết.

Nếu bài kiểm tra cần phải được viết theo cách nào đó, hãy viết nó theo cách đó. Cố gắng buộc bài kiểm tra vào một số bố cục kiểm tra lý tưởng hóa hoặc không có nó hoàn toàn không phải là một điều tốt. Có một bài kiểm tra hôm nay kiểm tra nó tốt hơn là kiểm tra "hoàn hảo" vào một ngày sau đó.

Tôi cũng sẽ chỉ ra một chút về bài kiểm tra xấu xí:

Khi mã xấu, các bài kiểm tra có thể xấu.

Bạn không muốn viết các bài kiểm tra xấu xí, nhưng mã xấu xí cần kiểm tra nhiều nhất.

Đừng để mã xấu ngăn bạn viết bài kiểm tra, nhưng hãy để mã xấu ngăn bạn viết thêm.

Đây có thể được coi là những sự thật đối với những người đã theo dõi trong một thời gian dài ... và họ đã ăn sâu vào cách suy nghĩ và viết bài kiểm tra. Đối với những người chưa và đang cố gắng đến thời điểm đó, lời nhắc có thể hữu ích (tôi thậm chí còn đọc lại chúng giúp tôi tránh bị mắc kẹt trong một số giáo điều).


Hãy xem xét rằng khi viết một bài kiểm tra xấu xí, nếu mã đó có thể là một dấu hiệu cho thấy mã đang cố gắng làm quá nhiều. Nếu mã mà bạn đang kiểm tra quá phức tạp để được thực hiện đúng bằng cách viết một bài kiểm tra đơn giản, bạn có thể muốn xem xét việc chia mã thành các phần nhỏ hơn có thể được kiểm tra bằng các bài kiểm tra đơn giản hơn. Người ta không nên viết một bài kiểm tra đơn vị làm tất cả mọi thứ (sau đó có thể không phải là một bài kiểm tra đơn vị ). Giống như 'các đối tượng của thần' là xấu, 'các bài kiểm tra đơn vị thần' cũng rất tệ và nên là dấu hiệu để quay lại và xem lại mã.

Bạn sẽ có thể thực hiện tất cả các mã với phạm vi bảo hiểm hợp lý thông qua các thử nghiệm đơn giản như vậy. Các thử nghiệm thực hiện nhiều thử nghiệm từ đầu đến cuối để xử lý các câu hỏi lớn hơn ("Tôi có đối tượng này, được sắp xếp thành xml, gửi đến dịch vụ web, thông qua các quy tắc, thoát ra và không được xử lý") là một thử nghiệm tuyệt vời - nhưng chắc chắn không phải vậy 'là một thử nghiệm đơn vị (và rơi vào lĩnh vực thử nghiệm tích hợp - ngay cả khi nó có các dịch vụ giả định mà nó gọi và tùy chỉnh trong cơ sở dữ liệu bộ nhớ để thực hiện thử nghiệm). Nó vẫn có thể sử dụng khung XUnit để thử nghiệm, nhưng khung thử nghiệm không biến nó thành thử nghiệm đơn vị.


7

Tôi đang thêm một câu trả lời mới vì quan điểm của tôi khác với khi tôi viết câu hỏi và câu trả lời ban đầu; không có ý nghĩa gì để chia chúng lại thành một.

Tôi đã nói trong câu hỏi ban đầu

Tuy nhiên, tôi không thể thấy bất cứ điều gì sai với thiết kế của mình (bạn tạo ra một danh sách các đối tượng dữ liệu khác như thế nào, một số giá trị của chúng phụ thuộc vào vị trí của chúng trong chuỗi? Chúng có thể tạo và kiểm tra chúng một cách riêng biệt)

Đây là nơi tôi đã đi sai. Sau khi thực hiện lập trình chức năng cho năm ngoái, bây giờ tôi nhận ra rằng tôi chỉ cần một hoạt động thu thập với một bộ tích lũy. Sau đó, tôi có thể viết hàm của mình như một hàm thuần túy hoạt động trên một thứ và sử dụng một số hàm thư viện chuẩn để áp dụng nó vào bộ sưu tập.

Vì vậy, câu trả lời mới của tôi là: sử dụng các kỹ thuật lập trình chức năng và bạn sẽ tránh được vấn đề này hoàn toàn. Bạn có thể viết các chức năng của mình để vận hành trên những thứ đơn lẻ và chỉ áp dụng chúng cho các bộ sưu tập của những thứ ở thời điểm cuối cùng. Nhưng nếu chúng là thuần túy, bạn có thể kiểm tra chúng mà không cần tham khảo các bộ sưu tập.

Đối với logic phức tạp hơn, dựa vào các bài kiểm tra dựa trên tài sản . Khi chúng có logic, nó phải nhỏ hơn và ngược với logic của mã đang được kiểm tra và mỗi thử nghiệm xác minh nhiều hơn một thử nghiệm đơn vị dựa trên trường hợp rằng lượng logic nhỏ đáng giá.

Trên hết luôn luôn dựa vào các loại của bạn . Lấy các loại mạnh nhất bạn có thể và sử dụng chúng để lợi thế của bạn. Điều này sẽ làm giảm số lượng bài kiểm tra bạn phải viết ở vị trí đầu tiên.


4

Đừng cố kiểm tra quá nhiều thứ cùng một lúc. Mỗi thuộc tính của từng đối tượng dữ liệu trong bộ sưu tập là quá nhiều cho một thử nghiệm. Thay vào đó, tôi khuyên bạn nên:

  1. Nếu bộ sưu tập có độ dài cố định, hãy viết một bài kiểm tra đơn vị để xác nhận độ dài. Nếu nó có độ dài thay đổi, hãy viết một số bài kiểm tra về độ dài sẽ đặc trưng cho hành vi của nó (ví dụ 0, 1, 3, 10). Dù bằng cách nào, không xác nhận tính chất trong các thử nghiệm này.
  2. Viết một bài kiểm tra đơn vị để xác nhận từng thuộc tính. Nếu bộ sưu tập có độ dài cố định và ngắn, chỉ cần xác nhận đối với một thuộc tính của từng yếu tố cho mỗi thử nghiệm. Nếu nó có độ dài cố định nhưng dài, hãy chọn một đại diện nhưng mẫu nhỏ của các yếu tố để khẳng định đối với mỗi thuộc tính. Nếu nó có độ dài thay đổi, hãy tạo một bộ sưu tập tương đối ngắn nhưng đại diện (có thể là ba yếu tố) và khẳng định đối với một thuộc tính của mỗi thuộc tính.

Làm theo cách này làm cho các xét nghiệm đủ nhỏ để bỏ đi các vòng không có vẻ đau đớn. C # / Đơn vị ví dụ, phương thức đã cho đang thử nghiệm ICollection<Foo> generateFoos(uint numberOfFoos):

[Test]
void generate_zero_foos_returns_empty_list() { ... }
void generate_one_foo_returns_list_of_one_foo() { ... }
void generate_three_foos_returns_list_of_three_foos() { ... }
void generated_foos_have_sequential_ID()
{
    var foos = generateFoos(3).GetEnumerable();
    foos.MoveNext();
    Assert.AreEqual("ID1", foos.Current.id);
    foos.MoveNext();
    Assert.AreEqual("ID2", foos.Current.id);
    foos.MoveNext();
    Assert.AreEqual("ID3", foos.Current.id);
}
void generated_foos_have_bar()
{
    var foos = generateFoos(3).GetEnumerable();
    foos.MoveNext();
    Assert.AreEqual("baz", foos.Current.bar);
    foos.MoveNext();
    Assert.AreEqual("baz", foos.Current.bar);
    foos.MoveNext();
    Assert.AreEqual("baz", foos.Current.bar);
}

Nếu bạn đã quen với mô hình "thử nghiệm đơn vị phẳng" (không có cấu trúc / logic lồng nhau), các thử nghiệm này có vẻ khá sạch sẽ. Do đó, logic được tránh trong các thử nghiệm bằng cách xác định vấn đề ban đầu là cố gắng kiểm tra quá nhiều thuộc tính cùng một lúc, thay vì thiếu các vòng lặp.


1
Osherove sẽ có một cái đầu của bạn trên một đĩa để có 3 khẳng định. ;) Cái đầu tiên thất bại có nghĩa là bạn không bao giờ xác nhận phần còn lại. Cũng lưu ý rằng bạn đã không thực sự tránh vòng lặp. Bạn chỉ cần mở rộng nó ra thành hình thức thực hiện của nó. Không phải là một lời chỉ trích nặng nề, nhưng chỉ là một gợi ý để có thêm một số thực hành cách ly các trường hợp thử nghiệm của bạn đến mức tối thiểu có thể, để cung cấp cho bạn thông tin phản hồi cụ thể hơn khi có điều gì đó không thành công, trong khi tiếp tục xác nhận các trường hợp khác vẫn có thể vượt qua (hoặc thất bại, với phản hồi cụ thể của riêng họ).
Anthony Pegram

3
@AnthonyPegram Tôi biết về mô hình thử nghiệm một lần khẳng định. Tôi thích câu thần chú "thử nghiệm một điều" (như được ủng hộ bởi Bob Martin, chống lại một thử thách một lần, trong Clean Code ). Lưu ý bên lề: các khung kiểm tra đơn vị có "kỳ vọng" so với "khẳng định" là tốt (Google Tests). Đối với phần còn lại, tại sao bạn không chia các đề xuất của mình thành một câu trả lời đầy đủ, với các ví dụ? Tôi nghĩ rằng tôi có thể có lợi.
Kazark
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.