Thực hiện các bài kiểm tra đơn vị nối tiếp (thay vì song song)


96

Tôi đang cố gắng kiểm tra đơn vị một công cụ quản lý máy chủ WCF mà tôi đã viết. Về cơ bản, engine tạo ra các phiên bản ServiceHost một cách nhanh chóng dựa trên cấu hình. Điều này cho phép chúng tôi định cấu hình lại động những dịch vụ nào khả dụng mà không cần phải gỡ bỏ tất cả và khởi động lại chúng bất cứ khi nào một dịch vụ mới được thêm vào hoặc một dịch vụ cũ bị xóa.

Tuy nhiên, tôi đã gặp khó khăn trong việc kiểm tra đơn vị công cụ quản lý máy chủ này do cách hoạt động của ServiceHost. Nếu một ServiceHost đã được tạo, mở và chưa đóng cho một điểm cuối cụ thể, thì không thể tạo một ServiceHost khác cho cùng một điểm cuối, dẫn đến một ngoại lệ. Vì thực tế là các nền tảng kiểm thử đơn vị hiện đại song song thực hiện thử nghiệm của chúng, tôi không có cách nào hiệu quả để kiểm tra đơn vị đoạn mã này.

Tôi đã sử dụng xUnit.NET, hy vọng rằng do khả năng mở rộng của nó, tôi có thể tìm ra cách để buộc nó chạy các thử nghiệm nối tiếp nhau. Tuy nhiên, tôi chưa gặp may mắn. Tôi hy vọng rằng ai đó ở đây trên SO đã gặp phải sự cố tương tự và biết cách làm cho các bài kiểm tra đơn vị chạy nối tiếp.

LƯU Ý: ServiceHost là một lớp WCF, được viết bởi Microsoft. Tôi không có khả năng thay đổi hành vi của nó. Lưu trữ mỗi điểm cuối dịch vụ chỉ một lần cũng là hành vi thích hợp ... tuy nhiên, nó không đặc biệt có lợi cho việc thử nghiệm đơn vị.


Hành vi cụ thể này của ServiceHost có phải là điều bạn có thể muốn giải quyết không?
Robert Harvey

ServiceHost được viết bởi Microsoft. Tôi không kiểm soát được nó. Và về mặt kỹ thuật, đó là hành vi hợp lệ ... bạn không bao giờ được có nhiều hơn một ServiceHost cho mỗi điểm cuối.
jrista 10/09/09

1
Tôi đã gặp sự cố tương tự khi cố gắng chạy nhiều TestServertrong docker. Vì vậy, tôi đã phải nối tiếp các bài kiểm tra tích hợp.
h-rai

Câu trả lời:


114

Mỗi lớp thử nghiệm là một tập hợp thử nghiệm duy nhất và các thử nghiệm dưới lớp đó sẽ chạy theo trình tự, vì vậy nếu bạn đặt tất cả các thử nghiệm của mình vào cùng một tập hợp thì nó sẽ chạy tuần tự.

Trong xUnit, bạn có thể thực hiện các thay đổi sau để đạt được điều này:

Sau đây sẽ chạy song song:

namespace IntegrationTests
{
    public class Class1
    {
        [Fact]
        public void Test1()
        {
            Console.WriteLine("Test1 called");
        }

        [Fact]
        public void Test2()
        {
            Console.WriteLine("Test2 called");
        }
    }

    public class Class2
    {
        [Fact]
        public void Test3()
        {
            Console.WriteLine("Test3 called");
        }

        [Fact]
        public void Test4()
        {
            Console.WriteLine("Test4 called");
        }
    }
}

Để làm cho nó tuần tự, bạn chỉ cần đặt cả hai lớp thử nghiệm trong cùng một tập hợp:

namespace IntegrationTests
{
    [Collection("Sequential")]
    public class Class1
    {
        [Fact]
        public void Test1()
        {
            Console.WriteLine("Test1 called");
        }

        [Fact]
        public void Test2()
        {
            Console.WriteLine("Test2 called");
        }
    }

    [Collection("Sequential")]
    public class Class2
    {
        [Fact]
        public void Test3()
        {
            Console.WriteLine("Test3 called");
        }

        [Fact]
        public void Test4()
        {
            Console.WriteLine("Test4 called");
        }
    }
}

Để biết thêm thông tin, bạn có thể tham khảo liên kết này


23
Tôi nghĩ câu trả lời không được đánh giá cao. Có vẻ hoạt động và tôi thích độ chi tiết, vì tôi có các bài kiểm tra song song và không thể song song trong một lắp ráp.
Igand

1
Đây là cách chính xác để thực hiện việc này, tham khảo tài liệu Xunit.
Håkon K. Olafsen

2
Đây phải là câu trả lời được chấp nhận vì thông thường một số bài kiểm tra có thể chạy song song (trong trường hợp của tôi là tất cả các bài kiểm tra đơn vị), nhưng một số lỗi ngẫu nhiên khi chạy song song (trong trường hợp của tôi là những bài kiểm tra sử dụng máy khách / máy chủ web trong bộ nhớ), vì vậy một là có thể tối ưu hóa việc chạy thử nghiệm nếu muốn.
Alexei

2
Điều này không hiệu quả với tôi trong một dự án lõi .net, nơi tôi thực hiện kiểm tra tích hợp với cơ sở dữ liệu sqlite. Các thử nghiệm vẫn được thực hiện song song. Tuy nhiên, câu trả lời được chấp nhận đã hoạt động.
user1796440,

Cảm ơn bạn rất nhiều vì câu trả lời này! Cần phải làm điều này vì tôi có Kiểm tra chấp nhận trong các lớp khác nhau mà cả hai đều kế thừa từ cùng một TestBase và đồng thời không hoạt động tốt với EF Core.
Kyanite

104

Như đã nêu ở trên, tất cả các bài kiểm tra đơn vị tốt nên được cách ly 100%. Sử dụng trạng thái chia sẻ (ví dụ: tùy thuộc vào thuộc statictính được sửa đổi theo từng thử nghiệm) được coi là hành vi xấu.

Đã nói rằng, câu hỏi của bạn về việc chạy các bài kiểm tra xUnit theo trình tự đã có câu trả lời! Tôi gặp chính xác vấn đề tương tự vì hệ thống của tôi sử dụng bộ định vị dịch vụ tĩnh (ít hơn lý tưởng).

Theo mặc định xUnit 2.x chạy tất cả các thử nghiệm song song. Điều này có thể được sửa đổi trên mỗi assembly bằng cách xác định CollectionBehaviortrong AssemblyInfo.cs trong dự án thử nghiệm của bạn.

Để sử dụng phân tách từng cụm:

using Xunit;
[assembly: CollectionBehavior(CollectionBehavior.CollectionPerAssembly)]

hoặc hoàn toàn không sử dụng song song:

[assembly: CollectionBehavior(DisableTestParallelization = true)]

Cái sau có lẽ là cái bạn muốn. Có thể tìm thêm thông tin về cấu hình và song song trên tài liệu xUnit .


5
Đối với tôi, có các tài nguyên được chia sẻ giữa các phương thức trong mỗi lớp. Chạy một bài kiểm tra từ một lớp, sau đó từ lớp khác, sẽ phá vỡ các bài kiểm tra của cả hai. Tôi đã có thể giải quyết bằng cách sử dụng [assembly: CollectionBehavior(CollectionBehavior.CollectionPerClass, DisableTestParallelization = true)]. Nhờ bạn, @Squiggle, tôi có thể chạy tất cả các bài kiểm tra của mình và đi uống cà phê! :)
Alielson Piffer

2
Câu trả lời từ Abhinav Saxena chi tiết hơn cho .NET Core.
Yennefer

64

Đối với các dự án .NET Core, hãy tạo xunit.runner.jsonbằng:

{
  "parallelizeAssembly": false,
  "parallelizeTestCollections": false
}

Ngoài ra, bạn csprojnên chứa

<ItemGroup>
  <None Update="xunit.runner.json"> 
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
  </None>
</ItemGroup>

Đối với các dự án .Net Core cũ, bạn project.jsonnên chứa

"buildOptions": {
  "copyToOutput": {
    "include": [ "xunit.runner.json" ]
  }
}

2
Tôi giả sử tương đương lõi dotnet csproj mới nhất sẽ là <ItemGroup><None Include="xunit.runner.json" CopyToOutputDirectory="Always" /></ItemGroup>hoặc tương tự?
Squiggle

3
Điều này làm việc cho tôi trong csproj:<ItemGroup> <None Update="xunit.runner.json"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </None> </ItemGroup>
Skynyrd

Việc vô hiệu hóa song song có hoạt động với Lý thuyết xUnit không?
John Zabroski

Đây là điều duy nhất hiệu quả với tôi, tôi đã thử chạy dotnet test --no-build -c Release -- xunit.parallelizeTestCollections=falsenhưng nó không hiệu quả với tôi.
Harvey

18

Đối với các dự án .NET Core, bạn có thể định cấu hình xUnit bằng một xunit.runner.jsontệp, như được ghi lại tại https://xunit.github.io/docs/configuring-with-json.html .

Cài đặt bạn cần thay đổi để dừng thực thi thử nghiệm song song là parallelizeTestCollections, mặc định là true:

Đặt giá trị này thành truenếu lắp ráp sẵn sàng chạy các thử nghiệm bên trong lắp ráp này song song với nhau. ... Đặt điều này để falsetắt tất cả song song trong cụm thử nghiệm này.

Loại lược đồ JSON: boolean
Giá trị mặc định:true

Vì vậy, mức tối thiểu xunit.runner.jsoncho mục đích này trông giống như

{
    "parallelizeTestCollections": false
}

Như đã lưu ý trong tài liệu, hãy nhớ bao gồm tệp này trong bản dựng của bạn, bằng cách:

  • Đặt Copy to Output Directory to Copy nếu mới hơn trong Thuộc tính của tệp trong Visual Studio, hoặc
  • Thêm

    <Content Include=".\xunit.runner.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </Content>

    vào .csprojtệp của bạn , hoặc

  • Thêm

    "buildOptions": {
      "copyToOutput": {
        "include": [ "xunit.runner.json" ]
      }
    }

    vào project.jsontệp của bạn

tùy thuộc vào loại dự án của bạn.

Cuối cùng, ngoài những điều trên, nếu bạn đang sử dụng Visual Studio thì hãy đảm bảo rằng bạn không vô tình nhấp vào nút Chạy thử nghiệm trong song song , nút này sẽ khiến các thử nghiệm chạy song song ngay cả khi bạn đã tắt chế độ song song xunit.runner.json. Các nhà thiết kế giao diện người dùng của Microsoft đã khéo léo làm cho nút này không được gắn nhãn, khó nhận thấy và cách nút "Chạy tất cả" khoảng một cm trong Test Explorer, chỉ để tối đa hóa cơ hội bạn sẽ nhấn nhầm và không biết tại sao các thử nghiệm của bạn đột nhiên không thành công:

Ảnh chụp màn hình với nút được khoanh tròn


@JohnZabroski Tôi không hiểu chỉnh sửa đề xuất của bạn . ReSharper có liên quan gì? Tôi nghĩ rằng tôi có thể đã cài đặt nó khi tôi viết câu trả lời ở trên, nhưng không phải mọi thứ ở đây đều không phụ thuộc vào việc bạn có đang sử dụng nó hay không? Gì trang web mà bạn liên kết đến trong chỉnh sửa phải làm với chỉ định một xunit.runner.jsontập tin? Và việc chỉ định một xunit.runner.jsonliên quan gì đến việc thực hiện các thử nghiệm chạy nối tiếp?
Mark Amery

Tôi đang cố gắng để các bài kiểm tra của mình chạy nối tiếp và ban đầu tôi nghĩ rằng vấn đề liên quan đến ReSharper (vì ReSharper KHÔNG có nút "Chạy thử nghiệm trong song song" như Visual Studio Test Explorer). Tuy nhiên, có vẻ như khi tôi sử dụng [Lý thuyết], các bài kiểm tra của tôi không bị cô lập. Điều này thật kỳ lạ, bởi vì mọi thứ tôi đọc đều cho thấy Class là đơn vị có thể song song hóa nhỏ nhất.
John Zabroski

8

Đây là câu hỏi cũ nhưng tôi muốn viết một giải pháp cho những người mới tìm kiếm như tôi :)

Lưu ý: Tôi sử dụng phương pháp này trong các bài kiểm tra tích hợp Dot Net Core WebUI với xunit phiên bản 2.4.1.

Tạo một lớp trống có tên NonParallelCollectionDefinitionClass và sau đó cung cấp thuộc tính CollectionDefinition cho lớp này như bên dưới. (Phần quan trọng là DisableParallelization = true setting.)

using Xunit;

namespace WebUI.IntegrationTests.Common
{
    [CollectionDefinition("Non-Parallel Collection", DisableParallelization = true)]
    public class NonParallelCollectionDefinitionClass
    {
    }
}

Sau đó thêm thuộc tính Collection vào lớp mà bạn không muốn nó chạy song song như bên dưới. (Phần quan trọng là tên của bộ sưu tập. Nó phải giống với tên được sử dụng trong CollectionDefinition)

namespace WebUI.IntegrationTests.Controllers.Users
{
    [Collection("Non-Parallel Collection")]
    public class ChangePassword : IClassFixture<CustomWebApplicationFactory<Startup>>
    ...

Khi chúng tôi làm điều này, trước hết các thử nghiệm song song khác sẽ chạy. Sau đó, các bài kiểm tra khác có thuộc tính Bộ sưu tập ("Bộ sưu tập không song song") sẽ chạy.


6

bạn có thể Sử dụng Danh sách phát

nhấp chuột phải vào phương pháp kiểm tra -> Thêm vào danh sách phát -> Danh sách phát mới

thì bạn có thể chỉ định thứ tự thực hiện, mặc định là như vậy, khi bạn thêm chúng vào danh sách phát nhưng bạn có thể thay đổi tệp danh sách phát theo ý muốn

nhập mô tả hình ảnh ở đây


5

Tôi không biết chi tiết, nhưng có vẻ như bạn đang cố gắng thực hiện thử nghiệm tích hợp hơn là thử nghiệm đơn vị . Nếu bạn có thể tách biệt phần phụ thuộc vào ServiceHost, điều đó có thể sẽ giúp việc thử nghiệm của bạn dễ dàng hơn (và nhanh hơn). Vì vậy, (ví dụ) bạn có thể kiểm tra những điều sau một cách độc lập:

  • Lớp đọc cấu hình
  • Nhà máy ServiceHost (có thể là một thử nghiệm tích hợp)
  • Lớp động cơ có một IServiceHostFactoryvà mộtIConfiguration

Các công cụ sẽ giúp bao gồm các khung cô lập (mô phỏng) và (tùy chọn) các khung chứa IoC. Xem:


Tôi không cố gắng thực hiện kiểm tra tích hợp. Tôi thực sự cần phải kiểm tra đơn vị. Tôi thông thạo các điều khoản và thực hành TDD / BDD (IoC, DI, Mocking, v.v.), vì vậy việc chạy các loại công cụ như tạo nhà máy và sử dụng giao diện không phải là điều tôi cần (nó đã được thực hiện, ngoại trừ trường hợp của chính ServiceHost.) ServiceHost không phải là một phần phụ thuộc có thể bị cô lập, vì nó không thể mô phỏng đúng cách (như nhiều không gian tên Hệ thống .NET.) Tôi thực sự cần một cách để chạy các thử nghiệm đơn vị nối tiếp.
jrista 11/09/09

1
@jrista - không có ý định về kỹ năng của bạn. Tôi không phải là nhà phát triển WCF, nhưng liệu công cụ có thể trả về một trình bao bọc xung quanh ServiceHost với một giao diện trên trình bao bọc không? Hoặc có lẽ là một nhà máy tùy chỉnh cho ServiceHosts?
TrueWill 11/09/09

Công cụ lưu trữ không trả về bất kỳ ServiceHosts nào. Nó thực sự không trả về bất cứ thứ gì, nó chỉ quản lý việc tạo, mở và đóng ServiceHosts trong nội bộ. Tôi có thể bao gồm tất cả các loại WCF cơ bản, nhưng đó là rất nhiều công việc mà tôi chưa thực sự được phép làm. Ngoài ra, hóa ra, sự cố không phải do thực hiện song song và sẽ vẫn xảy ra trong quá trình hoạt động bình thường. Tôi đã bắt đầu một câu hỏi khác ở đây trên SO về vấn đề này, và hy vọng tôi sẽ nhận được câu trả lời.
jrista 11/09/09

@TrueWill: BTW, tôi không lo lắng về việc bạn giảm kỹ năng của tôi chút nào ... Tôi chỉ không muốn nhận được nhiều câu trả lời sơ sài về tất cả những thứ phổ biến về kiểm thử đơn vị. Tôi cần một câu trả lời nhanh về một vấn đề rất cụ thể. Xin lỗi nếu tôi tỏ ra hơi cộc cằn, không phải chủ ý của tôi. Tôi chỉ có khá ít thời gian để làm cho việc này hoạt động.
jrista 11/09/09

3

Có thể bạn có thể sử dụng Kiểm tra đơn vị nâng cao . Nó cho phép bạn xác định trình tự mà bạn chạy thử nghiệm . Vì vậy, bạn có thể phải tạo một tệp cs mới để lưu trữ các thử nghiệm đó.

Đây là cách bạn có thể bẻ cong các phương pháp kiểm tra để hoạt động theo trình tự bạn muốn.

[Test]
[Sequence(16)]
[Requires("POConstructor")]
[Requires("WorkOrderConstructor")]
public void ClosePO()
{
  po.Close();

  // one charge slip should be added to both work orders

  Assertion.Assert(wo1.ChargeSlipCount==1,
    "First work order: ChargeSlipCount not 1.");
  Assertion.Assert(wo2.ChargeSlipCount==1,
    "Second work order: ChargeSlipCount not 1.");
  ...
}

Hãy cho tôi biết liệu nó có hoạt động không.


Bài báo tuyệt vời. Tôi thực sự đã đánh dấu nó trên CP. Cảm ơn vì liên kết, nhưng hóa ra, vấn đề dường như sâu hơn nhiều, vì những người chạy thử nghiệm dường như không chạy thử nghiệm song song.
jrista 11/09/09

2
Chờ đã, đầu tiên bạn nói rằng bạn không muốn chạy thử nghiệm song song, và sau đó bạn nói rằng vấn đề là người chạy thử nghiệm không chạy thử nghiệm song song ... vậy đó là cái nào?
Graviton

Liên kết bạn cung cấp không còn hoạt động. Và đây có phải là điều bạn có thể làm với xunit?
Allen Wang


0

Tôi đã thêm thuộc tính [Bộ sưu tập ("Tuần tự")] trong một lớp cơ sở:

    namespace IntegrationTests
    {
      [Collection("Sequential")]
      public class SequentialTest : IDisposable
      ...


      public class TestClass1 : SequentialTest
      {
      ...
      }

      public class TestClass2 : SequentialTest
      {
      ...
      }
    }

0

Không có câu trả lời được đề xuất nào cho đến nay phù hợp với tôi. Tôi có một ứng dụng lõi dotnet với XUnit 2.4.1. Tôi đã đạt được hành vi mong muốn với một giải pháp thay thế bằng cách đặt một khóa trong mỗi bài kiểm tra đơn vị. Trong trường hợp của tôi, tôi không quan tâm đến thứ tự chạy, chỉ cần các bài kiểm tra là tuần tự.

public class TestClass
{
    [Fact]
    void Test1()
    {
        lock (this)
        {
            //Test Code
        }
    }

    [Fact]
    void Test2()
    {
        lock (this)
        {
            //Test Code
        }
    }
}
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.