xUnit.net: Thiết lập toàn cầu + chia nhỏ?


98

Câu hỏi này là về khuôn khổ kiểm thử đơn vị xUnit.net .

Tôi cần chạy một số mã trước khi bất kỳ thử nghiệm nào được thực hiện và một số mã sau khi tất cả các thử nghiệm được thực hiện. Tôi nghĩ rằng cần phải có một số loại thuộc tính hoặc giao diện đánh dấu để chỉ ra mã khởi tạo và kết thúc toàn cầu, nhưng không thể tìm thấy chúng.

Ngoài ra, nếu tôi gọi xUnit theo lập trình, tôi cũng có thể đạt được những gì tôi muốn với đoạn mã sau:

static void Main()
{
    try
    {
        MyGlobalSetup();
        RunAllTests();  // What goes into this method?
    }
    finally
    {
        MyGlobalTeardown();
    }
}

Bất cứ ai có thể cung cấp cho tôi một gợi ý về cách khai báo hoặc lập trình chạy một số mã thiết lập / xé nhỏ toàn cầu không?


1
Tôi đoán đây là câu trả lời: stackoverflow.com/questions/12379949/...
the_joric

Câu trả lời:


118

Theo như tôi biết, xUnit không có điểm mở rộng khởi tạo / xé nhỏ toàn cầu. Tuy nhiên, rất dễ dàng để tạo một cái. Chỉ cần tạo một lớp kiểm tra cơ sở triển khai IDisposablevà thực hiện khởi tạo của bạn trong hàm tạo và chia nhỏ của bạn trong IDisposable.Disposephương thức. Nó sẽ giống như thế này:

public abstract class TestsBase : IDisposable
{
    protected TestsBase()
    {
        // Do "global" initialization here; Called before every test method.
    }

    public void Dispose()
    {
        // Do "global" teardown here; Called after every test method.
    }
}

public class DummyTests : TestsBase
{
    // Add test methods
}

Tuy nhiên, thiết lập lớp cơ sở và mã chia nhỏ sẽ được thực hiện cho mỗi cuộc gọi. Đây có thể không phải là điều bạn muốn, vì nó không hiệu quả lắm. Một phiên bản được tối ưu hóa hơn sẽ sử dụng IClassFixture<T>giao diện để đảm bảo rằng chức năng khởi tạo / chia nhỏ toàn cục chỉ được gọi một lần. Đối với phiên bản này, bạn không mở rộng lớp cơ sở từ lớp thử nghiệm của mình nhưng triển khai IClassFixture<T>giao diện Ttham chiếu đến lớp cố định của bạn:

using Xunit;

public class TestsFixture : IDisposable
{
    public TestsFixture ()
    {
        // Do "global" initialization here; Only called once.
    }

    public void Dispose()
    {
        // Do "global" teardown here; Only called once.
    }
}

public class DummyTests : IClassFixture<TestsFixture>
{
    public DummyTests(TestsFixture data)
    {
    }
}

Điều này sẽ dẫn đến hàm tạo TestsFixturechỉ được chạy một lần cho mọi lớp được kiểm tra. Do đó, nó phụ thuộc vào những gì bạn muốn chính xác để lựa chọn giữa hai phương pháp.


4
Có vẻ như IUseFixture không còn tồn tại nữa, đã được thay thế bằng IClassFixture.
GaTechThomas

9
Trong khi điều này hoạt động, tôi nghĩ CollectionFixture trong câu trả lời từ Geir Sagberg phù hợp hơn với kịch bản này vì nó được thiết kế đặc biệt cho mục đích này. Bạn cũng không cần phải kế thừa lớp học thử nghiệm của bạn, bạn chỉ cần đánh dấu chúng với các [Collection("<name>")]thuộc tính
MichelZ

8
Có cách nào để thực hiện thiết lập không đồng bộ và chia nhỏ không?
Andrii

Có vẻ như MS cũng đã triển khai giải pháp IClassFixture. docs.microsoft.com/en-us/aspnet/core/test/...
lbrahim

3
XUnit cung cấp ba tùy chọn khởi tạo: mỗi phương pháp thử nghiệm, mỗi lớp thử nghiệm và mở rộng một số lớp thử nghiệm. Tài liệu ở đây: xunit.net/docs/shared-context
GHN

48

Tôi đang tìm kiếm câu trả lời tương tự và tại thời điểm này, tài liệu xUnit rất hữu ích về cách triển khai Đồ đạc lớp và Đồ đạc bộ sưu tập cung cấp cho các nhà phát triển nhiều chức năng thiết lập / chia nhỏ ở cấp độ lớp hoặc nhóm lớp. Điều này phù hợp với câu trả lời từ Geir Sagberg và cung cấp cách triển khai khung xương tốt để minh họa nó trông như thế nào.

https://xunit.github.io/docs/shared-context.html

Bộ sưu tập Khi nào sử dụng: khi bạn muốn tạo một ngữ cảnh thử nghiệm duy nhất và chia sẻ nó giữa các thử nghiệm trong một số lớp thử nghiệm và dọn dẹp nó sau khi tất cả các thử nghiệm trong các lớp thử nghiệm đã kết thúc.

Đôi khi bạn sẽ muốn chia sẻ một đối tượng cố định giữa nhiều lớp thử nghiệm. Ví dụ về cơ sở dữ liệu được sử dụng cho đồ đạc lớp là một ví dụ tuyệt vời: bạn có thể muốn khởi tạo cơ sở dữ liệu với một tập dữ liệu thử nghiệm, sau đó để dữ liệu thử nghiệm đó tại chỗ để nhiều lớp thử nghiệm sử dụng. Bạn có thể sử dụng tính năng cố định bộ sưu tập của xUnit.net để chia sẻ một cá thể đối tượng duy nhất giữa các thử nghiệm trong một số lớp thử nghiệm.

Để sử dụng các thiết bị thu gom, bạn cần thực hiện các bước sau:

Tạo lớp fixture và đặt mã khởi động trong hàm tạo lớp fixture. Nếu lớp fixture cần thực hiện dọn dẹp, hãy triển khai IDisposable trên lớp fixture và đặt mã dọn dẹp trong phương thức Dispose (). Tạo lớp định nghĩa bộ sưu tập, trang trí nó bằng thuộc tính [CollectionDefinition], đặt cho nó một tên duy nhất sẽ xác định bộ sưu tập thử nghiệm. Thêm ICollectionFixture <> vào lớp định nghĩa tập hợp. Thêm thuộc tính [Collection] vào tất cả các lớp thử nghiệm sẽ là một phần của tập hợp, sử dụng tên duy nhất mà bạn đã cung cấp cho thuộc tính [CollectionDefinition] của lớp định nghĩa tập hợp thử nghiệm. Nếu các lớp thử nghiệm cần quyền truy cập vào cá thể fixture, hãy thêm nó làm đối số của hàm tạo và nó sẽ được cung cấp tự động. Đây là một ví dụ đơn giản:

public class DatabaseFixture : IDisposable
{
    public DatabaseFixture()
    {
        Db = new SqlConnection("MyConnectionString");

        // ... initialize data in the test database ...
    }

    public void Dispose()
    {
        // ... clean up test data from the database ...
    }

    public SqlConnection Db { get; private set; }
}

[CollectionDefinition("Database collection")]
public class DatabaseCollection : ICollectionFixture<DatabaseFixture>
{
    // This class has no code, and is never created. Its purpose is simply
    // to be the place to apply [CollectionDefinition] and all the
    // ICollectionFixture<> interfaces.
}

[Collection("Database collection")]
public class DatabaseTestClass1
{
    DatabaseFixture fixture;

    public DatabaseTestClass1(DatabaseFixture fixture)
    {
        this.fixture = fixture;
    }
}

[Collection("Database collection")]
public class DatabaseTestClass2
{
    // ...
}

xUnit.net xử lý các đồ đạc tập hợp theo cách giống như đồ đạc lớp, ngoại trừ thời gian tồn tại của một đối tượng cố định tập hợp dài hơn: nó được tạo trước khi chạy bất kỳ thử nghiệm nào trong bất kỳ lớp thử nghiệm nào trong tập hợp và sẽ không bị xóa cho đến khi tất cả các lớp thử nghiệm trong bộ sưu tập chạy xong.

Các bộ sưu tập thử nghiệm cũng có thể được trang trí bằng IClassFixture <>. xUnit.net coi điều này như thể mỗi lớp kiểm tra riêng lẻ trong bộ sưu tập bài kiểm tra đã được trang trí bằng lịch thi đấu của lớp.

Các bộ sưu tập kiểm tra cũng ảnh hưởng đến cách xUnit.net chạy các bài kiểm tra khi chạy chúng song song. Để biết thêm thông tin, hãy xem Chạy thử nghiệm song song.

Lưu ý quan trọng: Các đồ đạc phải ở trong cùng một bộ phận với thử nghiệm sử dụng chúng.


1
"Các bộ sưu tập thử nghiệm cũng có thể được trang trí bằng IClassFixture <>. XUnit.net xử lý điều này như thể mỗi lớp thử nghiệm riêng lẻ trong bộ sưu tập thử nghiệm được trang trí bằng lớp cố định." Bất kỳ cơ hội nào tôi có thể lấy một ví dụ về điều đó? Tôi không hoàn toàn hiểu nó.
rtf

@TannerFaulkner Lớp cố định là một cách để thiết lập và chia nhỏ mức CLASS, giống như bạn làm với Dự án kiểm tra đơn vị .net truyền thống khi bạn có phương pháp Khởi tạo thử nghiệm: [TestInitialize] public void Initialize () {
Larry Smith

Vấn đề duy nhất mà tôi gặp phải với điều này là bạn cần trang trí các lớp thử nghiệm của mình bằng Collectionthuộc tính để thiết lập "toàn cầu" xảy ra. Điều đó có nghĩa là, nếu bạn có bất cứ thứ gì bạn muốn thiết lập trước khi chạy-nhiều-test, bạn cần trang trí các lớp -all- test với thuộc tính này. Điều này theo tôi là quá khó, vì việc quên trang trí một lớp kiểm tra đơn lẻ có thể dẫn đến những sai sót khó theo dõi. Sẽ thật tuyệt nếu xUnit tạo ra một cách để thiết lập và chia nhỏ toàn cầu thực sự.
Zodman

13

Có một giải pháp dễ dàng dễ dàng. Sử dụng plugin Fody.ModuleInit

https://github.com/Fody/ModuleInit

Đó là một gói nuget và khi bạn cài đặt nó, nó sẽ thêm một tệp mới có tên là ModuleInitializer.cs vào dự án. Có một phương thức tĩnh ở đây được đưa vào lắp ráp sau khi xây dựng và được chạy ngay sau khi lắp ráp được tải và trước khi mọi thứ được chạy.

Tôi sử dụng quyền này để mở khóa giấy phép phần mềm cho thư viện mà tôi đã mua. Tôi luôn quên mở khóa giấy phép trong mỗi bài kiểm tra và thậm chí quên lấy bài kiểm tra từ một lớp cơ sở sẽ mở khóa nó. Những tia lửa sáng đã viết thư viện này, thay vì nói với bạn rằng giấy phép bị khóa đã đưa ra các lỗi số tinh vi khiến các bài kiểm tra không thành công hoặc vượt qua khi không nên. Bạn sẽ không bao giờ biết liệu bạn đã mở khóa thư viện đúng cách hay chưa. Vì vậy, bây giờ init mô-đun của tôi trông giống như

/// <summary>
/// Used by the ModuleInit. All code inside the Initialize method is ran as soon as the assembly is loaded.
/// </summary>
public static class ModuleInitializer
{
    /// <summary>
    /// Initializes the module.
    /// </summary>
    public static void Initialize()
    {
            SomeLibrary.LicenceUtility.Unlock("XXXX-XXXX-XXXX-XXXX-XXXX");
    }
}

và tất cả các bài kiểm tra được đưa vào lắp ráp này sẽ có giấy phép được mở khóa chính xác cho chúng.


2
Ý tưởng vững chắc; tiếc là nó dường như chưa hoạt động với các thử nghiệm đơn vị DNX.
Jeff Dunlop

12

Để chia sẻ mã SetUp / TearDown giữa nhiều lớp, bạn có thể sử dụng CollectionFixture của xUnit .

Trích dẫn:

Để sử dụng các thiết bị thu gom, bạn cần thực hiện các bước sau:

  • Tạo lớp fixture và đặt mã khởi động trong hàm tạo lớp fixture.
  • Nếu lớp fixture cần thực hiện dọn dẹp, hãy triển khai IDisposable trên lớp fixture và đặt mã dọn dẹp trong phương thức Dispose ().
  • Tạo lớp định nghĩa bộ sưu tập, trang trí nó bằng thuộc tính [CollectionDefinition], đặt cho nó một tên duy nhất sẽ xác định bộ sưu tập thử nghiệm.
  • Thêm ICollectionFixture <> vào lớp định nghĩa tập hợp.
  • Thêm thuộc tính [Collection] vào tất cả các lớp thử nghiệm sẽ là một phần của tập hợp, sử dụng tên duy nhất mà bạn đã cung cấp cho thuộc tính [CollectionDefinition] của lớp định nghĩa tập hợp thử nghiệm.
  • Nếu các lớp thử nghiệm cần quyền truy cập vào cá thể fixture, hãy thêm nó làm đối số của hàm tạo và nó sẽ được cung cấp tự động.
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.