2008-12-03 20 views
11

Hiện tại tôi đang bắt đầu giới thiệu khái niệm về các đối tượng Mock vào Bài kiểm tra Đơn vị của tôi. Đặc biệt tôi đang sử dụng khung công tác Moq. Tuy nhiên, một trong những điều tôi đã nhận thấy là đột nhiên các lớp tôi đang thử nghiệm bằng cách sử dụng khung này đang hiển thị mức độ bao phủ mã là 0%.Làm cách nào tôi có thể sử dụng Đối tượng Mock trong các thử nghiệm đơn vị của mình và vẫn sử dụng Phạm vi mã?

Bây giờ tôi hiểu rằng vì tôi chỉ đang chế nhạo lớp, nên nó không tự chạy lớp thực tế .... nhưng làm cách nào để viết các bài kiểm tra này và có Mã trả về kết quả chính xác? Tôi có phải viết một bộ kiểm tra sử dụng Mocks và một bộ để khởi tạo lớp trực tiếp không.

Có lẽ tôi đang làm điều gì đó sai trái mà không nhận ra nó?

Dưới đây là một ví dụ về tôi cố gắng Unit Test một lớp được gọi là "MyClass":

using Moq; 
using NUnitFramework; 

namespace MyNameSpace 
{ 
    [TestFixture] 
    public class MyClassTests 
    { 

     [Test] 
     public void TestGetSomeString() 
     { 
      const string EXPECTED_STRING = "Some String!"; 

      Mock<MyClass> myMock = new Mock<MyClass>(); 
      myMock.Expect(m => m.GetSomeString()).Returns(EXPECTED_STRING); 

      string someString = myMock.Object.GetSomeString(); 

      Assert.AreEqual(EXPECTED_STRING, someString); 
      myMock.VerifyAll(); 

     } 

    } 

    public class MyClass 
    { 
     public virtual string GetSomeString() 
     { 
      return "Hello World!"; 
     } 
    } 
} 

Có ai biết những gì tôi nên làm khác đi?

Trả lời

15

Bạn không sử dụng đúng đối tượng giả lập. Khi bạn đang sử dụng các đối tượng giả, bạn muốn kiểm tra cách mã của bạn tương tác với các đối tượng khác mà không thực sự sử dụng các đối tượng thực. Xem mã bên dưới:

using Moq; 
using NUnitFramework; 

namespace MyNameSpace 
    { 
     [TestFixture] 
     public class MyClassTests 
     { 

      [Test] 
      public void TestGetSomeString() 
      { 
       const string EXPECTED_STRING = "Some String!"; 

       Mock<IDependance> myMock = new Mock<IDependance>(); 
       myMock.Expect(m => m.GiveMeAString()).Returns("Hello World"); 

       MyClass myobject = new MyClass(); 

       string someString = myobject.GetSomeString(myMock.Object); 

       Assert.AreEqual(EXPECTED_STRING, someString); 
       myMock.VerifyAll(); 

      } 

     } 

     public class MyClass 
     { 

      public virtual string GetSomeString(IDependance objectThatITalkTo) 
      { 
       return objectThatITalkTo.GiveMeAString(); 
      } 
     } 

     public interface IDependance 
     { 
      string GiveMeAString(); 
     } 
    } 

Dường như nó đang làm bất kỳ điều gì hữu ích khi mã của bạn chỉ trả lại chuỗi mà không có bất kỳ logic nào đằng sau nó.

Quyền lực thực sự đến nếu bạn GetSomeString() phương pháp đã thực hiện một số logic có thể thay đổi kết quả của chuỗi đầu ra tùy thuộc vào sự trở lại từ IDependdance. Phương pháp GiveMeAString(), sau đó bạn có thể xem cách phương thức xử lý dữ liệu xấu được gửi từ giao diện IDependdance.

Cái gì như:

public virtual string GetSomeString(IDependance objectThatITalkTo { 
    if (objectThatITalkTo.GiveMeAString() == "Hello World") 
    return "Hi"; 
} 

Bây giờ nếu bạn có dòng này trong thử nghiệm của bạn:

myMock.Expect(m => m.GiveMeAString()).Returns(null); 

gì sẽ xảy ra với phương pháp GetSomeString() của bạn?

+0

Thiếu khung trong ví dụ GetSomeString của bạn sau thông số. –

6

Sai lầm lớn là chế nhạo số System Under Test (SUT), bạn kiểm tra điều gì đó khác. Bạn chỉ nên giả lập phụ thuộc SUT.

0

Điều đó có ý nghĩa rất nhiều. Về cơ bản bạn đang nói rằng tôi cần phải được làm như sau:

public class MyClass 
{ 
    public virtual string GetSomeString(MyOtherClass moc) 
    { 
     return moc.ToString(); 
    } 
} 

..... 

Mock<MyOtherClass> myMock = new Mock<MyOtherClass>(); 

MyClass mc = new MyClass(); 

string someString = mc.GetSomeString(myMock.Object); 
Assert.AreEqual(EXPECTED_STRING, someString); 

Về cơ bản instantiating SUT và chỉ sử dụng mocks cho các lớp các SUT đòi hỏi?

+0

Cộng với phương thức GetSomeString của bạn bây giờ sẽ có vùng phủ sóng vì bạn đang gọi GetSomeString thực nhưng chỉ với một đối tượng giả nó cần để bạn không đi sâu hơn và tung ra bất kỳ tên lửa nào mà bạn không biết về :) –

2

Tôi khuyên bạn nên tránh xa khung mocking cho đến khi bạn hiểu các tương tác đang diễn ra tại đây.

IMO tốt hơn là bạn nên học với đôi kiểm tra được tạo theo cách thủ công, sau đó chuyển sang khuôn khổ mocking sau đó. Lý do của tôi:

  1. Mocking framework trừu tượng những gì đang xảy ra; dễ nắm bắt các tương tác hơn nếu bạn phải tạo phụ thuộc của bạn một cách rõ ràng, sau đó làm theo các thử nghiệm trong trình gỡ lỗi.

  2. Thật dễ dàng để lạm dụng các khuôn khổ. Nếu bạn cuộn của riêng bạn khi bạn đang học, bạn có nhiều khả năng hiểu được sự khác biệt giữa các loại kiểm tra tăng gấp đôi. Nếu bạn đi thẳng đến một khuôn khổ mocking, thật dễ dàng để sử dụng mocks khi bạn muốn khai và ngược lại - có một sự khác biệt lớn.

Hãy suy nghĩ theo cách này: Lớp được kiểm tra là tiêu điểm. Bạn tạo một thể hiện của nó, gọi phương thức của nó và sau đó xác nhận rằng kết quả là chính xác. Nếu lớp được thử nghiệm có các phụ thuộc (ví dụ: một cái gì đó được yêu cầu trong hàm tạo), bạn thỏa mãn những phụ thuộc đó bằng cách sử dụng A: các lớp thực hoặc B: kiểm tra tăng gấp đôi.

Lý do chúng tôi sử dụng kiểm tra tăng gấp đôi là nó cô lập các lớp được kiểm tra, có nghĩa là bạn có thể thực hiện mã của nó theo cách được kiểm soát nhiều hơn.

Ví dụ: nếu bạn có một lớp có chứa một đối tượng mạng, bạn không thể kiểm tra các thói quen xử lý lỗi của lớp riêng mà phát hiện các kết nối chết nếu bạn buộc phải sử dụng một đối tượng kết nối mạng cụ thể. Thay vào đó, bạn tiêm một đối tượng kết nối giả mạo và yêu cầu nó ném một ngoại lệ khi phương thức "SendBytes" của nó được gọi.

I.e. Trong mỗi bài kiểm tra, các phụ thuộc của lớp được kiểm tra được tạo ra đặc biệt để thực hiện một đoạn mã cụ thể.

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