Đơn vị thử nghiệm phương pháp void?


170

Cách tốt nhất để kiểm tra đơn vị một phương thức không trả về bất cứ điều gì là gì? Cụ thể trong c #.

Những gì tôi thực sự đang cố gắng kiểm tra là một phương thức lấy tệp nhật ký và phân tích cú pháp cho các chuỗi cụ thể. Các chuỗi sau đó được chèn vào cơ sở dữ liệu. Không có gì chưa được thực hiện trước đây nhưng RẤT mới đối với TDD Tôi tự hỏi liệu có thể kiểm tra điều này hay nó là thứ gì đó không thực sự được kiểm tra.


55
Vui lòng không sử dụng thuật ngữ "TDD" nếu đó không phải là điều bạn đang làm. Bạn đang làm bài kiểm tra đơn vị, không phải TDD. Nếu bạn đang làm TDD, bạn sẽ không bao giờ có câu hỏi như "làm thế nào để kiểm tra một phương pháp". Bài kiểm tra sẽ tồn tại trước tiên, và sau đó câu hỏi sẽ là "làm thế nào để bài kiểm tra này vượt qua?" Nhưng nếu bạn đang làm TDD, mã của bạn sẽ được viết cho bài kiểm tra (không phải theo cách khác) và về cơ bản bạn sẽ trả lời câu hỏi của riêng bạn. Mã của bạn sẽ được định dạng khác nhau do kết quả của TDD và vấn đề này sẽ không bao giờ xảy ra. Chỉ cần làm rõ.
Suamere

Câu trả lời:


151

Nếu một phương thức không trả về bất cứ điều gì, thì đó là một trong những cách sau

  • bắt buộc - Bạn đang yêu cầu đối tượng tự làm gì đó .. ví dụ: thay đổi trạng thái (không mong đợi bất kỳ xác nhận nào .. giả định rằng nó sẽ được thực hiện)
  • thông tin - chỉ cần thông báo cho ai đó rằng một cái gì đó đã xảy ra (mà không mong đợi hành động hoặc phản ứng) tương ứng.

Phương pháp bắt buộc - bạn có thể xác minh nếu tác vụ thực sự được thực hiện. Xác minh nếu thay đổi trạng thái thực sự diễn ra. ví dụ

void DeductFromBalance( dAmount ) 

có thể được kiểm tra bằng cách xác minh nếu số dư đăng thông báo này thực sự nhỏ hơn giá trị ban đầu của dAmount

Các phương thức thông tin - hiếm khi là thành viên của giao diện chung của đối tượng ... do đó thường không được kiểm tra đơn vị. Tuy nhiên nếu bạn phải, Bạn có thể xác minh nếu việc xử lý được thực hiện trên thông báo diễn ra. ví dụ

void OnAccountDebit( dAmount )  // emails account holder with info

có thể được kiểm tra bằng cách xác minh nếu email đang được gửi

Đăng thêm chi tiết về phương pháp thực tế của bạn và mọi người sẽ có thể trả lời tốt hơn.
Cập nhật : Phương pháp của bạn đang làm 2 việc. Tôi thực sự đã chia nó thành hai phương pháp hiện có thể được kiểm tra độc lập.

string[] ExamineLogFileForX( string sFileName );
void InsertStringsIntoDatabase( string[] );

Chuỗi [] có thể được xác minh dễ dàng bằng cách cung cấp phương thức đầu tiên với tệp giả và chuỗi dự kiến. Cách thứ hai hơi khó khăn .. bạn có thể sử dụng Mock (google hoặc tìm kiếm stackoverflow trên khung mô phỏng) để bắt chước DB hoặc nhấn DB thực tế và xác minh xem các chuỗi được chèn vào đúng vị trí. Kiểm tra chủ đề này để biết một số cuốn sách hay ... Tôi muốn giới thiệu Kiểm tra đơn vị thực dụng nếu bạn đang ở trong tình trạng khủng hoảng.
Trong mã nó sẽ được sử dụng như

InsertStringsIntoDatabase( ExamineLogFileForX( "c:\OMG.log" ) );

1
này gishu, câu trả lời tốt Ví dụ bạn đã đưa ra ... không phải họ có nhiều Bài kiểm tra tích hợp hơn sao ...? và nếu vậy, câu hỏi vẫn còn, làm thế nào một người thực sự kiểm tra Phương pháp Void .... có lẽ điều đó là không thể?
andy

2
@andy - phụ thuộc vào định nghĩa của bạn về 'kiểm tra tích hợp'. Các phương thức bắt buộc thường thay đổi trạng thái, do đó bạn có thể được xác minh bằng một bài kiểm tra đơn vị để thẩm vấn trạng thái của đối tượng. Phương pháp thông tin có thể được xác minh bằng một bài kiểm tra đơn vị cắm vào trình nghe / cộng tác viên giả để đảm bảo rằng đối tượng kiểm tra đưa ra thông báo đúng. Tôi nghĩ rằng cả hai có thể được kiểm tra hợp lý thông qua các bài kiểm tra đơn vị.
Gishu

@andy cơ sở dữ liệu có thể được mô phỏng / phân tách bằng giao diện truy cập, do đó cho phép hành động được kiểm tra bởi dữ liệu được truyền đến đối tượng giả.
Peter Geiger

62

Kiểm tra tác dụng phụ của nó. Điêu nay bao gôm:

  • Nó có ném ngoại lệ nào không? (Nếu cần, hãy kiểm tra xem có đúng không. Nếu không, hãy thử một số trường hợp góc có thể nếu bạn không cẩn thận - đối số null là điều rõ ràng nhất.)
  • Nó chơi độc đáo với các thông số của nó? (Nếu chúng có thể thay đổi, nó có biến đổi chúng khi không nên và ngược lại không?)
  • Nó có ảnh hưởng đúng đến trạng thái của đối tượng / loại bạn đang gọi nó không?

Tất nhiên, có giới hạn về số lượng bạn có thể kiểm tra. Bạn thường không thể kiểm tra với mọi đầu vào có thể, ví dụ. Kiểm tra thực tế - đủ để bạn tự tin rằng mã của bạn được thiết kế phù hợp và được triển khai chính xác và đủ để đóng vai trò là tài liệu bổ sung cho những gì người gọi có thể mong đợi.


31

Như mọi khi: kiểm tra những gì phương pháp được cho là phải làm!

Nó có nên thay đổi trạng thái toàn cầu (uuh, mùi mã!) Ở đâu đó không?

Nó có nên gọi vào một giao diện không?

Nó có nên ném một ngoại lệ khi được gọi với các tham số sai?

Nó có nên ném ngoại lệ khi được gọi với các tham số đúng không?

Có nên ...?


11

Các kiểu trả về trống / Chương trình con là tin cũ. Tôi đã không thực hiện kiểu trả về Void (Trừ khi tôi cực kỳ lười biếng) trong vòng 8 năm (Từ thời điểm trả lời này, vì vậy chỉ cần một chút trước khi câu hỏi này được hỏi).

Thay vì một phương thức như:

public void SendEmailToCustomer()

Tạo một phương thức theo mô hình int.TryPude () của Microsoft:

public bool TrySendEmailToCustomer()

Có thể không có bất kỳ thông tin nào mà phương thức của bạn cần trả về để sử dụng trong thời gian dài, nhưng việc trả lại trạng thái của phương thức sau khi nó thực hiện công việc của nó là một công dụng rất lớn đối với người gọi.

Ngoài ra, bool không phải là loại trạng thái duy nhất. Có một số lần khi một Chương trình con được tạo trước đó thực sự có thể trả về ba hoặc nhiều trạng thái khác nhau (Tốt, Bình thường, Xấu, v.v.). Trong những trường hợp đó, bạn chỉ cần sử dụng

public StateEnum TrySendEmailToCustomer()

Tuy nhiên, trong khi Try-Paradigm phần nào trả lời câu hỏi này về cách kiểm tra trả lại khoảng trống, thì cũng có những cân nhắc khác. Ví dụ: trong / sau chu kỳ "TDD", bạn sẽ "Tái cấu trúc" và nhận thấy bạn đang làm hai việc với phương pháp của mình ... do đó phá vỡ "Nguyên tắc trách nhiệm duy nhất". Vì vậy, cần được chăm sóc đầu tiên. Thứ hai, bạn có thể đã xác định được sự phụ thuộc ... bạn đang chạm vào Dữ liệu "Liên tục".

Nếu bạn đang thực hiện các công cụ truy cập dữ liệu trong câu hỏi phương thức, bạn cần cấu trúc lại thành kiến ​​trúc n-tier'd hoặc n-layer'd. Nhưng chúng ta có thể giả sử rằng khi bạn nói "Các chuỗi sau đó được chèn vào cơ sở dữ liệu", bạn thực sự có nghĩa là bạn đang gọi một lớp logic nghiệp vụ hoặc một cái gì đó. Ya, chúng tôi sẽ cho rằng.

Khi đối tượng của bạn được khởi tạo, bây giờ bạn hiểu rằng đối tượng của bạn có các phụ thuộc. Đây là khi bạn cần quyết định xem bạn sẽ thực hiện Dependency Injection trên Object hay trên Phương thức. Điều đó có nghĩa là Trình xây dựng của bạn hoặc phương thức trong câu hỏi cần một Tham số mới:

public <Constructor/MethodName> (IBusinessDataEtc otherLayerOrTierObject, string[] stuffToInsert)

Giờ đây, bạn có thể chấp nhận giao diện của đối tượng tầng dữ liệu / doanh nghiệp của mình, bạn có thể mô phỏng nó trong Kiểm tra đơn vị và không phụ thuộc hoặc sợ thử nghiệm tích hợp "Tình cờ".

Vì vậy, trong mã sống của bạn, bạn vượt qua trong một IBusinessDataEtcđối tượng THỰC SỰ . Nhưng trong Kiểm tra đơn vị của bạn, bạn vượt qua trong một IBusinessDataEtcđối tượng MOCK . Trong Mock đó, bạn có thể bao gồm các thuộc tính không giao diện như int XMethodWasCalledCounthoặc một cái gì đó có trạng thái được cập nhật khi các phương thức giao diện được gọi.

Vì vậy, Bài kiểm tra đơn vị của bạn sẽ đi qua (các) Phương thức của bạn, thực hiện bất kỳ logic nào họ có và gọi một hoặc hai hoặc một nhóm phương thức được chọn trong IBusinessDataEtcđối tượng của bạn . Khi bạn thực hiện các Xác nhận của mình khi kết thúc Bài kiểm tra đơn vị, bạn có một vài điều cần kiểm tra ngay bây giờ.

  1. Trạng thái của "Chương trình con" hiện là phương pháp Thử-Nghịch lý.
  2. Trạng thái của IBusinessDataEtcđối tượng Mock của bạn .

Để biết thêm thông tin về các ý tưởng Tiêm phụ thuộc ở cấp độ Xây dựng ... khi chúng liên quan đến Thử nghiệm đơn vị ... hãy xem xét các mẫu thiết kế của Builder. Nó bổ sung thêm một giao diện và lớp cho mỗi giao diện / lớp hiện tại mà bạn có, nhưng chúng rất nhỏ và cung cấp chức năng HUGE tăng để Kiểm tra đơn vị tốt hơn.


Đoạn đầu tiên của câu trả lời tuyệt vời này đóng vai trò là lời khuyên chung tuyệt vời cho tất cả các lập trình viên mới / trung cấp.
pimbrouwers

nó không phải là một cái gì đó như thế public void sendEmailToCustomer() throws UndeliveredMailExceptionnào?
A.Emad

1
@ A.Emad Câu hỏi hay, nhưng không. Kiểm soát luồng mã của bạn bằng cách dựa vào các ngoại lệ ném là một thực tiễn xấu được biết đến từ lâu. Mặc dù, trong voidcác phương thức, đặc biệt là các ngôn ngữ hướng đối tượng, nó là lựa chọn thực sự duy nhất. Sự thay thế lâu đời nhất của Microsoft là Mô hình thử mà tôi thảo luận và các mô hình kiểu chức năng như Monads / Maybes. Do đó, các Lệnh (Trong CQS) vẫn có thể trả về thông tin trạng thái có giá trị thay vì dựa vào việc ném, tương tự như một GOTO(mà chúng ta biết là xấu). ném (và goto) là chậm, khó khăn để gỡ lỗi, và thực hành không tốt.
Suamere

Cảm ơn bạn đã làm rõ điều này. Là C # cụ thể hay nói chung là thực tế tồi để ném ngoại lệ ngay cả trong các ngôn ngữ như Java và C ++?
A.Emad

9

Thử cái này:

[TestMethod]
public void TestSomething()
{
    try
    {
        YourMethodCall();
        Assert.IsTrue(true);
    }
    catch {
        Assert.IsTrue(false);
    }
}

1
Điều này không cần thiết nhưng có thể được thực hiện như vậy
Nathan Alard

Chào mừng bạn đến với StackOverflow! Vui lòng xem xét thêm một số giải thích cho mã của bạn. Cảm ơn bạn.
Aurasphere

2
Các ExpectedAttributeđược thiết kế để thực hiện xét nghiệm này rõ ràng hơn.
Martin Liversage

8

Bạn thậm chí có thể thử nó theo cách này:

[TestMethod]
public void ReadFiles()
{
    try
    {
        Read();
        return; // indicates success
    }
    catch (Exception ex)
    {
        Assert.Fail(ex.Message);
    }
}

1
Đây là cách đơn giản nhất mà tôi cho là.
Navin Pandit

5

nó sẽ có một số hiệu ứng trên một đối tượng .... truy vấn cho kết quả của hiệu ứng. Nếu nó không có hiệu ứng rõ ràng, nó không có giá trị thử nghiệm đơn vị!


4

Có lẽ phương pháp làm một cái gì đó, và không chỉ đơn giản là trở lại?

Giả sử đây là trường hợp, sau đó:

  1. Nếu nó sửa đổi trạng thái của đối tượng chủ sở hữu, thì bạn nên kiểm tra xem trạng thái đã thay đổi chính xác chưa.
  2. Nếu nó lấy một số đối tượng làm tham số và sửa đổi đối tượng đó, thì bạn nên kiểm tra đối tượng được sửa đổi chính xác.
  3. Nếu nó ném ngoại lệ là một số trường hợp nhất định, hãy kiểm tra xem những ngoại lệ đó có được ném chính xác không.
  4. Nếu hành vi của nó thay đổi dựa trên trạng thái của đối tượng của chính nó hoặc một số đối tượng khác, hãy đặt trước trạng thái và kiểm tra phương thức đó có đúng một trong ba phương pháp thử nghiệm ở trên).

Nếu bạn cho chúng tôi biết phương pháp làm gì, tôi có thể cụ thể hơn.


3

Sử dụng Rhino Mocks để đặt những gì cuộc gọi, hành động và ngoại lệ có thể được mong đợi. Giả sử bạn có thể chế nhạo hoặc bỏ đi các phần của phương pháp. Khó biết mà không biết một số chi tiết cụ thể ở đây về phương pháp, hoặc thậm chí bối cảnh.


1
Câu trả lời cho cách kiểm tra đơn vị không bao giờ nên là lấy một công cụ của bên thứ ba làm điều đó cho bạn. Mặc dù, khi một người biết cách kiểm tra đơn vị, sử dụng công cụ của bên thứ ba để làm cho nó dễ dàng hơn là chấp nhận được.
Suamere

2

Phụ thuộc vào những gì nó đang làm. Nếu nó có các tham số, hãy chuyển qua các giả mà bạn có thể hỏi sau này nếu chúng được gọi với bộ tham số phù hợp.


Đồng ý - xác minh hành vi của các thử nghiệm giả phương pháp sẽ là một cách.
Jeff Schumacher

0

Bất kỳ trường hợp nào bạn đang sử dụng để gọi phương thức void, Bạn chỉ có thể sử dụng,Verfiy

Ví dụ:

Trong trường hợp của tôi, đây _Loglà ví dụ và LogMessagelà phương thức được kiểm tra:

try
{
    this._log.Verify(x => x.LogMessage(Logger.WillisLogLevel.Info, Logger.WillisLogger.Usage, "Created the Student with name as"), "Failure");
}
Catch 
{
    Assert.IsFalse(ex is Moq.MockException);
}

Là một Verifycú ném ngoại lệ do thất bại của phương pháp mà bài kiểm tra sẽ thất bại?

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.