2010-08-19 22 views
22

Câu hỏi này liên quan đến kỹ thuật kiểm tra đơn vị chung với một loạt các trường hợp có khả năng rất hữu ích. Nhưng dễ hiểu hơn với ví dụ minh họa câu hỏi của tôi tốt hơn.Việc xác định TestMethod có trong các lớp cơ sở thử nghiệm không được hỗ trợ bởi MsTest?

Giả sử tôi muốn kiểm tra tất cả các loại ghi đè Equals() sẽ thực hiện chính xác như vậy. Kể từ Equals() được định nghĩa là ảo trong System.Object, một loạt các loại có thể thay đổi hành vi đó. Mỗi loại làm như vậy, sẽ phải có các bài kiểm tra để đảm bảo rằng hành vi mới tuân theo các kỳ vọng tiềm ẩn của người gọi của phương thức đó. Cụ thể cho Equals(), nếu bạn ghi đè phương pháp đó, triển khai mới phải đảm bảo rằng hai đối tượng bằng nhau cũng có mã băm bằng nhau, như được xác định bởi System.Object.GetHashCode().

Vì vậy, để thực thi điều này, nhiều lớp thử nghiệm sẽ là cần thiết và tất cả chúng sẽ kiểm tra tính nhất quán của hành vi trên tất cả các loại này.

Để tránh phải gõ lại tất cả các TestMethods cần thiết để kiểm tra như một loại I thay vì định nghĩa một lớp thử nghiệm cơ bản trông giống như dưới đây, và có những lớp học thử nghiệm tất cả các thừa kế cùng bộ kiểm tra hành vi:

/// <summary> 
/// Test fixture base class for testing types that overrides Object.Equals() 
/// </summary> 
/// <typeparam name="T">The production type under test</typeparam> 
public abstract class EqualsFixtureBase<T> 
{ 
    #region Equals tests 

    protected static void CompareInstances(T inst1, T inst2, bool expectedEquals) 
    { 
     Assert.AreEqual(expectedEquals, inst1.Equals((T)inst2)); 
     Assert.AreEqual(expectedEquals, inst1.Equals((object)inst2)); 
     if (expectedEquals) 
     { 
      // equal instances MUST have identical hash codes 
      // this is a part of the .NET Equals contract 
      Assert.AreEqual(inst1.GetHashCode(), inst2.GetHashCode()); 
     } 
     else 
     { 
      if (inst2 != null) 
      { 
       Assert.AreNotEqual(inst1.GetHashCode(), inst2.GetHashCode()); 
      } 
     } 
    } 

    /// <summary> 
    /// Creates version 1 instance of the type under test, not 'Equal' to instance 2. 
    /// </summary> 
    /// <returns>An instance created with properties 1.</returns> 
    protected abstract T CreateInstance1(); 

    /// <summary> 
    /// Creates version 2 instance of the type under test, not 'Equal' to instance 1. 
    /// </summary> 
    /// <returns>An instance created with properties 2.</returns> 
    protected abstract T CreateInstance2(); 

    /// <summary> 
    /// Creates an instance equal to the version 1 instance, but not the identical 
    /// same object. 
    /// </summary> 
    /// <returns>An instance created with properties equal to instance 1.</returns> 
    protected abstract T CreateInstanceThatEqualsInstance1(); 

    [TestMethod] 
    public void Equals_NullOrDefaultValueTypeInstance() 
    { 
     T instance = CreateInstance1(); 
     CompareInstances(instance, default(T), false); 
    } 

    [TestMethod] 
    public void Equals_InstanceOfAnotherType() 
    { 
     T instance = CreateInstance1(); 
     Assert.IsFalse(instance.Equals(new object())); 
    } 

    [TestMethod] 
    public void Equals_SameInstance() 
    { 
     T slot1 = CreateInstance1(); 
     CompareInstances(slot1, slot1, true); 
    } 

    [TestMethod] 
    public void Equals_EqualInstances() 
    { 
     T slot1 = CreateInstance1(); 
     T slot2 = CreateInstanceThatEqualsInstance1(); 
     CompareInstances(slot1, slot2, true); 
     CompareInstances(slot2, slot1, true); 
    } 

    [TestMethod] 
    public void Equals_NonEqualInstances() 
    { 
     T slot1 = CreateInstance1(); 
     T slot2 = CreateInstance2(); 
     CompareInstances(slot1, slot2, false); 
     CompareInstances(slot2, slot1, false); 
    } 

    #endregion Equals tests 
} 

Sau đó tôi có thể sử dụng lại các TestMethods này cho mỗi loại ghi đè Equals(). Ví dụ, đây sẽ là định nghĩa lớp thử nghiệm để kiểm tra rằng các loại System.String thực hiện Equals() chính xác.

[TestClass] 
public class ExampleOfAnEqualsTestFixture : EqualsFixtureBase<string> 
{ 
    [TestMethod] 
    public void Foo() 
    { 
     Assert.IsTrue(true); 
    } 

    protected override string CreateInstance1() 
    { 
     return "FirstString"; 
    } 

    protected override string CreateInstance2() 
    { 
     return "SecondString"; 
    } 

    protected override string CreateInstanceThatEqualsInstance1() 
    { 
     return "FirstString"; 
    } 
} 

Điều này cũng có thể được mở rộng thêm. Ví dụ, đối với các kiểu quá tải các toán tử == và! =, Lớp cơ sở thử nghiệm trừu tượng thứ hai có thể được định nghĩa (ví dụ EqualsOperatorsFixtureBase<T> : EqualsFixtureBase<T>) để kiểm tra việc thực hiện các toán tử đó không chỉ đúng, mà còn phù hợp với định nghĩa mở rộng của Equals()GetHashCode().

Tôi có thể làm điều này bằng cách sử dụng NUnit, nhưng khi sử dụng MsTest tôi gặp sự cố.

a) Visual Studio 2010 chỉ phát hiện ra phương pháp thử nghiệm Foo(), không phải phương pháp thử nghiệm kế thừa để không thể chạy chúng. Dường như bộ tải thử nghiệm Visual Studio không đi theo hệ thống phân cấp thừa kế của lớp thử nghiệm.

b) Khi tôi kiểm tra các loại này trong TFS, TFS tìm loại EqualsFixtureBase trừu tượng và cho rằng đó là một lớp thử nghiệm sẽ được chạy. Nhưng vì nó không thể được tạo ra, nó không thể chạy nó và nhãn các bài kiểm tra trong loại đó là không xác định - mà không chạy thử nghiệm, và do đó xây dựng (!).

Có cách nào để giải quyết vấn đề này hay đây là hạn chế của MsTest và Visual Studio?

Nếu có, hãy sửa lỗi này trong lộ trình cho VS/TFS ?? Điều này sẽ rất hữu ích, đặc biệt là khi thử nghiệm các loại sản xuất thực hiện một giao diện, hoặc là một phần của một băng giá thừa kế, nơi các thành viên nhất định có thuộc tính 'hợp đồng loại' hoặc bất biến - nếu điều đó có ý nghĩa.

Về cơ bản, không có hỗ trợ cho điều này ngăn cản tôi từ việc tái cấu trúc mã thử nghiệm của tôi để xóa trùng lặp.

Cảm ơn

EDIT: Tôi tìm thấy this link đến một trong những blog MSDN, nó nói như sau

"Trong Whidbey, hỗ trợ cho các lớp học thử nghiệm thừa kế đã mất tích trong NUnit, nó được hỗ trợ đầy đủ. Điều này sẽ được sửa chữa ở Orcas. "

Điều đó đã được viết hơn ba năm trước. Tại sao điều này chưa được thêm vào? Tôi không hiểu, có lý do chính đáng để có điều này và trong tâm trí của tôi nó sẽ là một thay đổi nhỏ. Hay tôi chỉ không nhảy đúng vòng ở đây?

+1

@ Jack & Rory: Giai đoạn tiền thưởng kết thúc trong khi câu hỏi này vẫn chưa được giải quyết, vì vậy tôi sẽ không đánh dấu câu hỏi này là đã trả lời. Tuy nhiên bạn đã đóng góp tốt và cả hai phần đã giúp, vì vậy tôi đã bỏ phiếu cho mỗi câu trả lời tương ứng của bạn một khe. Nếu tôi hiểu các quy tắc một cách chính xác, điều này có nghĩa là bạn sẽ chia sẻ các điểm thưởng. – Mahol25

Trả lời

21

Sử dụng VS 2010 Tôi không thấy hành vi tương tự như bạn. Khi tôi sao chép 2 lớp học của bạn thành một dự án thử nghiệm và thu thập nó tôi có kết quả:

UTA004: Illegal use of attribute...The TestMethodAttribute can be 
defined only inside a class marked with the TestClass attribute 

Vì vậy, tôi đánh dấu EqualsFixutureBase:

[TestClass] 
public abstract class EqualsFixtureBase<T> 
{ 
... 
} 

Bây giờ nó biên dịch mà không báo trước và khi tôi chọn bài kiểm tra chạy cho ExampleOfAnEqualsTestFixture nó chạy Foo và tất cả 5 của các bài kiểm tra equals thừa kế. Ngoài ra khi tôi sao chép ExampleOfAnEqualsTestFixture và sử dụng nó cho int và chạy thử nghiệm cho giải pháp, tôi thấy tất cả 5 thử nghiệm kế thừa đang chạy (và chuyển) cho lớp chuỗi mẫu và lớp int mẫu.

Bạn đang làm điều gì đó ngoài ví dụ của bạn có thể gây ra sự cố?

+2

Tôi không nhận được kết quả bạn làm, không có cảnh báo trình biên dịch. Im sử dụng VS2010 Ultimate, Phiên bản 10.0.30319.1. Khi tôi thêm TestClassAttribute vào lớp cơ sở trừu tượng, TFS sẽ tìm thấy các phương thức thử nghiệm kế thừa, tốt hơn! Nhưng VS thử nghiệm cục bộ thì không. Có lẽ bạn đang chạy cục bộ bằng cách sử dụng resharper? Điều đó làm việc cho tôi quá, nhưng sẽ không giúp med khi tôi muốn xem thử nghiệm mã vùng địa phương kể từ khi tôi cần Á hậu thử nghiệm VS cho điều đó. :/ – Mahol25

+0

Tôi không sử dụng Test Runner, tôi chỉ vừa mới chạy thử nghiệm hoặc thông qua IDE hoặc chạy MSTest từ dấu nhắc lệnh. –

+1

Trên thực tế, tất cả các loại đều nằm trong cùng một dự án – Mahol25

6

Hết hộp, có vẻ như kế thừa kiểm tra đơn vị chỉ hoạt động nếu lớp kiểm tra cơ sở nằm trong cùng một nhóm với các lớp dẫn xuất. Đối với tôi, điều này thường đánh bại mục đích của việc có lớp cơ sở. Tôi cũng tự hỏi tại sao không được đăng nhiều hơn về điều này trên các blog, và nếu tôi có thể thiếu một cái gì đó.

Bạn có thể giải quyết được vấn đề bằng cách liên kết lớp cơ sở vào mọi dự án mà bạn muốn sử dụng nó. Có thể đánh dấu nó là nội bộ để nhiều bản sao không can thiệp lẫn nhau.

Ngoài ra còn có TestClassExtensionAttribute mà bạn có thể mở rộng để móc vào công cụ thực hiện kiểm tra. Tôi đã thử sử dụng nó để phản ánh qua các lớp thử nghiệm và tải các bài kiểm tra của lớp cơ sở, nhưng rất nhiều lớp học không có giấy tờ, và tôi không thể làm cho nó hoạt động được.

+2

Đây là ví dụ về blog TestClassExtensionAttribute: http://blogs.msdn.com/b/vstsqualitytools/archive/2009/09/04/extending-the-visual-studio-unit-test-type-part-1.aspx – ErnieL

0

Tính năng này có hoạt động nếu bạn đặt lắp ráp lớp cơ sở trong cùng thư mục với thư mục có nguồn gốc không? Có lẽ đó là lý do tại sao đặt chúng trong cùng một công trình lắp ráp; hội đồng khác không thể giải quyết được tại thời điểm họ muốn. Tôi không chắc chắn làm thế nào khác để thiết lập các đường dẫn thăm dò quyền bạn có thể cần, tại thời điểm họ là cần thiết. Các .testsettings có thể thể hiện những thứ như một appbase và thăm dò appdomain của á hậu, có lẽ những người thiết lập một cách chính xác sẽ giúp nó liên kết với các hội đồng lớp cơ sở nếu khác với tổ hợp thử nghiệm đơn vị dẫn xuất gốc.

+1

Mọi thứ trong cùng một hội đồng. Nhưng tại sao nó lại quan trọng nếu họ không ở đâu? CLR vẫn phải tải tất cả các loại trước khi thực hiện bất kỳ điều gì, và sau đó tất cả siêu dữ liệu loại có liên quan đều có sẵn – Mahol25

10

TestClassAttribute cho phép bạn đặt các phương thức trong cơ sở trừu tượng. Các IgnoreAttribute loại trừ các lớp cơ sở từ danh sách các bài kiểm tra. Nếu không có các phương thức thuộc tính IgnoreAttribute trong cơ sở được thực hiện cho cả lớp cơ sở và trong các lớp con được đánh dấu bằng TestClassAttribute,.

[TestClass][Ignore] 
public abstract class EqualsFixtureBase<T> 
{ 
.... 
Các vấn đề liên quan