2012-03-25 34 views
15

Hãy xem xét ví dụ sau về thử nghiệm đơn vị. Các ý kiến ​​khá nhiều giải thích vấn đề của tôi.Luôn làm sạch sau khi thử nghiệm?

[TestMethod] 
public void MyTestMethod() 
{ 

    //generate some objects in the database 
    ... 

    //make an assert that fails sometimes (for example purposes, this fails always) 
    Assert.IsTrue(false); 

    //TODO: how do we clean up the data generated in the database now that the test has ended here? 

} 
+0

Bạn cũng nên sử dụng 'TearDown' (hoặc tương đương trong bộ của bạn) để dọn sạch vì nếu kiểm tra của bạn không thành công, mã làm sạch sẽ không được thực hiện. –

+0

Làm thế nào để bắt một ngoại lệ, sau đó lại ném nó sau khi dọn dẹp? –

+0

Jimmy Bogard (tác giả của * AutoMapper *) có một bài viết và công cụ tuyệt vời: [Kiểm tra cơ sở dữ liệu đáng tin cậy với Respawn] (https://lostechies.com/jimmybogard/2015/02/19/reliable-database-tests-with-respawn /) –

Trả lời

24

Có hai cách để thực hiện việc này. One đang sử dụng thuộc tính TestInitialize và TestCleanup trên các phương thức trong lớp thử nghiệm. Họ sẽ luôn luôn được chạy trước và sau khi kiểm tra, tương ứng.

Một cách khác là sử dụng thực tế là các lỗi thử nghiệm được truyền đến nhân tố thử nghiệm thông qua các ngoại lệ. Điều này có nghĩa rằng một thử {} cuối cùng {} khối trong thử nghiệm của bạn có thể được sử dụng làm sạch bất cứ điều gì sau khi một khẳng định không thành công.

[TestMethod] 
public void FooTest() 
{ 
    try 
    { 
    // setup some database objects 
    Foo foo = new Foo(); 
    Bar bar = new Bar(foo); 
    Assert.Fail(); 
    } 
    finally 
    { 
    // remove database objects. 
    } 
} 

Việc thử/cuối cùng dọn dẹp có thể trở nên lộn xộn là có rất nhiều đối tượng để dọn dẹp. Những gì nhóm của tôi đã hướng tới là một lớp trợ giúp thực hiện IDisposable. Nó theo dõi các đối tượng đã được tạo ra và đẩy chúng vào một ngăn xếp. Khi Vứt bỏ được gọi là các mục được bật ra khỏi ngăn xếp và loại bỏ khỏi cơ sở dữ liệu.

[TestMethod] 
public void FooTest() 
{ 
    using (FooBarDatabaseContext context = new FooBarDatabaseContext()) 
    { 
    // setup some db objects. 
    Foo foo = context.NewFoo(); 
    Bar bar = context.NewBar(foo); 
    Assert.Fail(); 
    } // calls dispose. deletes bar, then foo. 
} 

Điều này có thêm lợi ích khi gói các hàm tạo trong các cuộc gọi phương thức. Nếu thay đổi chữ ký của hàm tạo, chúng ta có thể dễ dàng sửa đổi mã kiểm thử.

+9

Tôi thích nitpick; TestCleanup không chạy nếu TestInitialize đã ném một ngoại lệ. –

+4

@ carlin.scott Nặng nitpicking của bạn vừa được lưu trong ngày của tôi –

6

Một vài phản ứng:

  1. Nếu nó sử dụng một cơ sở dữ liệu thực tế, tôi muốn lập luận rằng nó không phải là một "đơn vị kiểm tra" theo nghĩa chặt chẽ của thuật ngữ. Đó là một thử nghiệm tích hợp. Một xét nghiệm đơn vị nên không có tác dụng phụ như vậy. Hãy xem xét sử dụng một thư viện mocking để mô phỏng cơ sở dữ liệu thực tế. Rhino Mocks là một, nhưng có rất nhiều người khác.

  2. Nếu, tuy nhiên, toàn bộ điểm của thử nghiệm này là thực sự tương tác với cơ sở dữ liệu, sau đó bạn sẽ muốn tương tác với cơ sở dữ liệu chỉ thử nghiệm tạm thời. Trong trường hợp đó, một phần của thử nghiệm tự động của bạn sẽ bao gồm mã để xây dựng cơ sở dữ liệu thử nghiệm từ đầu, sau đó chạy thử nghiệm, sau đó phá hủy cơ sở dữ liệu thử nghiệm. Một lần nữa, ý tưởng là không có tác dụng phụ bên ngoài. Có lẽ có nhiều cách để đi về điều này, và tôi không quen thuộc với các khung kiểm thử đơn vị để thực sự đưa ra một gợi ý cụ thể. Nhưng nếu bạn đang sử dụng thử nghiệm được xây dựng trong Visual Studio thì có lẽ Visual Studio Database Project sẽ được sử dụng.

1

Câu hỏi của bạn hơi quá chung chung. Thông thường bạn nên dọn sạch sau mỗi lần kiểm tra. Thông thường, bạn không thể tin tưởng rằng tất cả các kiểm thử luôn được thực thi theo thứ tự như nhau và bạn phải chắc chắn về những gì có trong cơ sở dữ liệu của bạn. Để thiết lập chung hoặc dọn dẹp hầu hết các khung kiểm tra đơn vị cung cấp các phương thức setUp và tearDown mà bạn có thể ghi đè và sẽ tự động được gọi. Tôi không biết nó hoạt động như thế nào trong C# nhưng e. g. trong JUnit (Java) bạn có các phương thức này.

Tôi đồng ý với David. Xét nghiệm của bạn thường không có tác dụng phụ. Bạn nên thiết lập một cơ sở dữ liệu mới cho mọi thử nghiệm đơn lẻ.

0

Bạn sẽ phải thực hiện dọn dẹp thủ công trong trường hợp này. Tức là, đối diện của việc tạo ra một số đối tượng trong db.

Cách khác, là sử dụng công cụ Mocking như Rhino Mocks để các cơ sở dữ liệu chỉ là một trong cơ sở dữ liệu bộ nhớ

0

Nó phụ thuộc vào những gì bạn đang thực sự thử nghiệm. Nhìn vào các ý kiến ​​tôi sẽ nói , nhưng bằng cách rất khó để khấu trừ tìm kiếm trên ý kiến. Làm sạch đối tượng bạn vừa chèn vào bạn, trong thực tế, đặt lại trạng thái của thử nghiệm. Vì vậy, nếu bạn dọn dẹp, bạn bắt đầu kiểm tra từ hệ thống dọn dẹp.

10

Tôi nghĩ câu trả lời hay nhất trong các tình huống như thế này là suy nghĩ rất kỹ về những gì bạn đang cố thử nghiệm. Lý tưởng nhất là một thử nghiệm đơn vị nên cố gắng kiểm tra một thực tế duy nhất về một phương pháp hoặc chức năng duy nhất. Khi bạn bắt đầu kết hợp nhiều thứ với nhau, nó sẽ đi qua thế giới của các bài kiểm tra tích hợp (có giá trị như nhau, nhưng khác nhau).

Đối với mục đích thử nghiệm đơn vị, để cho phép bạn chỉ kiểm tra điều bạn muốn kiểm tra, bạn sẽ cần phải thiết kế cho khả năng thử nghiệm. Điều này thường liên quan đến việc sử dụng thêm các giao diện (tôi giả sử .NET từ mã bạn đã cho thấy) và một số dạng tiêm phụ thuộc (nhưng không yêu cầu thùng chứa IoC/DI trừ khi bạn muốn một). Nó cũng được hưởng lợi từ, và khuyến khích bạn tạo các lớp học rất gắn kết (mục đích duy nhất) và tách riêng (phụ thuộc mềm) trong hệ thống của bạn.Vì vậy, khi bạn đang thử nghiệm logic nghiệp vụ phụ thuộc vào dữ liệu từ một cơ sở dữ liệu, bạn thường sẽ sử dụng một cái gì đó giống như Repository Pattern và tiêm một fake/stub/mock IXXXRepository để thử nghiệm đơn vị. Khi bạn đang thử nghiệm kho lưu trữ cụ thể, bạn cần phải thực hiện loại dọn dẹp cơ sở dữ liệu mà bạn đang hỏi hoặc bạn cần shim/stub cuộc gọi cơ sở dữ liệu bên dưới. Điều đó thực sự tùy thuộc vào bạn.

Khi bạn cần tạo/điền/dọn dẹp cơ sở dữ liệu, bạn có thể xem xét tận dụng các phương pháp thiết lập và ngăn nắp sẵn có trong hầu hết các khuôn khổ thử nghiệm. Nhưng hãy cẩn thận, bởi vì một số trong số chúng được chạy trước và sau mỗi bài kiểm tra, điều này có thể ảnh hưởng nghiêm trọng đến hiệu suất của các bài kiểm tra đơn vị của bạn. Các thử nghiệm chạy quá chậm sẽ không chạy rất thường xuyên, và điều đó là xấu.

Trong MS-Test, các thuộc tính bạn sẽ sử dụng để khai báo thiết lập/teardown là ClassInitialize, ClassCleanUp, TestInitialize, TestCleanUp. Các khung công tác khác có cấu trúc được đặt tên tương tự.

Có một số khuôn khổ có thể giúp bạn với mocking/stubbing: Moq, Rhino Mocks, NMock, TypeMock, Moles and Stubs (VS2010), VS11 Fakes (VS11 Beta), vv Nếu bạn đang tìm kiếm cho các khuôn khổ dependency injection, nhìn vào những thứ như Ninject, Unity, Castle Windsor, v.v.

0

Tôi nghĩ việc dọn dẹp phụ thuộc vào cách bạn đang tạo dữ liệu, vì vậy nếu "dữ liệu thử nghiệm cũ" không tương tác với các thử nghiệm trong tương lai, tôi nghĩ bạn nên bỏ qua nó.

Cách tiếp cận mà tôi đã thực hiện khi viết kiểm tra tích hợp là chạy thử nghiệm dựa vào một db khác với db ứng dụng. Tôi có xu hướng xây dựng lại db thử nghiệm như một điều kiện tiên quyết cho mỗi lần chạy thử nghiệm. Bằng cách đó bạn không cần một lược đồ làm sạch chi tiết cho mỗi bài kiểm tra khi mỗi lần chạy thử nghiệm nhận được một phương tiện sạch sẽ giữa các lần chạy. Hầu hết sự phát triển của tôi tôi đã thực hiện bằng cách sử dụng máy chủ SQL, nhưng tôi có trong một số trường hợp chạy thử nghiệm của tôi dựa trên một phiên bản SQL Compact db, nhanh chóng và hiệu quả để xây dựng lại giữa các lần chạy.

0

mbUnit có một thuộc tính rất tiện lợi Rollback làm sạch cơ sở dữ liệu sau khi hoàn thành thử nghiệm. Tuy nhiên, bạn sẽ phải cấu hình DTC (Điều phối viên giao dịch phân tán) để có thể sử dụng nó.

0

Tôi đã gặp phải vấn đề tương tự khi xác nhận của một thử nghiệm đang ngăn chặn việc dọn dẹp và khiến các thử nghiệm khác thất bại.

Hy vọng rằng điều này được sử dụng cho ai đó, đôi khi.

[Test] 
    public void Collates_Blah_As_Blah() 
    { 
     Assert.False(SINGLETON.Collection.Any()); 

     for (int i = 0; i < 2; i++) 
      Assert.That(PROCESS(ValidRequest) == Status.Success); 

     try 
     { 
      Assert.AreEqual(1, SINGLETON.Collection.Count); 
     } 
     finally 
     { 
      SINGLETON.Collection.Clear(); 
     } 
    } 

Khối cuối cùng sẽ thực thi xác nhận vượt qua hay không thành công, nó cũng không giới thiệu nguy cơ sai đường - việc bắt giữ sẽ gây ra!

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