단위 테스트를 병렬이 아닌 직렬로 실행합니다.


96

내가 작성한 WCF 호스트 관리 엔진을 단위 테스트하려고합니다. 엔진은 기본적으로 구성을 기반으로 즉석에서 ServiceHost 인스턴스를 만듭니다. 이를 통해 새로운 서비스가 추가되거나 이전 서비스가 제거 될 때마다 서비스를 모두 종료하고 다시 시작할 필요없이 사용할 수있는 서비스를 동적으로 재구성 할 수 있습니다.

그러나 ServiceHost의 작동 방식으로 인해이 호스트 관리 엔진을 단위 테스트하는 데 어려움이 있습니다. 특정 끝점에 대해 ServiceHost가 이미 생성되고 열렸지만 아직 닫히지 않은 경우 동일한 끝점에 대해 다른 ServiceHost를 만들 수 없으므로 예외가 발생합니다. 최신 단위 테스트 플랫폼은 테스트 실행을 병렬화하기 때문에이 코드 조각을 단위 테스트 할 효과적인 방법이 없습니다.

확장 성 때문에 테스트를 직렬로 실행하도록 강제하는 방법을 찾을 수 있기를 바라면서 xUnit.NET을 사용했습니다. 그러나 나는 운이 없었습니다. 나는 여기 누군가가 비슷한 문제를 겪고 단위 테스트를 직렬로 실행하는 방법을 알고 있기를 바랍니다.

참고 : ServiceHost 는 Microsoft에서 작성한 WCF 클래스입니다. 나는 그 행동을 바꿀 능력이 없습니다. 각 서비스 엔드 포인트를 한 번만 호스팅하는 것도 적절한 동작이지만 단위 테스트에는 특별히 도움이되지 않습니다.


ServiceHost의이 특정 동작이 해결하고 싶은 것이 아닐까요?
Robert Harvey

ServiceHost는 Microsoft에서 작성했습니다. 나는 그것을 통제 할 수 없습니다. 그리고 기술적으로 말하면 유효한 동작입니다. 엔드 포인트 당 하나 이상의 ServiceHost가 있어서는 안됩니다.
jrista 09.09.10

1
TestServer도커에서 여러 번 실행하려고 비슷한 문제가 발생 했습니다. 그래서 통합 테스트를 직렬화해야했습니다.
h-rai

답변:


114

각 테스트 클래스는 고유 한 테스트 컬렉션이며 그 아래의 테스트는 순서대로 실행되므로 모든 테스트를 동일한 컬렉션에 넣으면 순차적으로 실행됩니다.

xUnit에서는이를 위해 다음과 같이 변경할 수 있습니다.

다음은 병렬로 실행됩니다.

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");
        }
    }
}

순차적으로 만들려면 두 테스트 클래스를 동일한 컬렉션에 넣어야합니다.

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");
        }
    }
}

자세한 정보는 이 링크를 참조하세요.


23
감사하지 않은 대답이라고 생각합니다. 하나의 어셈블리에 병렬화 가능하고 병렬화 불가능한 테스트가 있기 때문에 작동하는 것 같고 세분성이 마음에 듭니다.
Igand

1
이것이 올바른 방법입니다. 참조 Xunit 문서.
Håkon K. Olafsen

2
일반적으로 일부 테스트는 병렬로 실행될 수 있지만 (내 경우에는 모든 단위 테스트) 일부 테스트는 병렬로 실행될 때 무작위로 실패하므로 (내 경우에는 인 메모리 웹 클라이언트 / 서버를 사용하는 경우) 이것이 허용되는 대답이어야합니다. 원하는 경우 테스트 실행을 최적화 할 수 있습니다.
알렉세이

2
sqlite 데이터베이스와의 통합 테스트를 수행하는 .net 핵심 프로젝트에서는 작동하지 않았습니다. 테스트는 여전히 병렬로 실행되었습니다. 받아 들여진 대답은 작동했습니다.
user1796440

이 답변에 감사드립니다! 동일한 TestBase에서 상속하고 동시성이 EF Core에서 잘 작동하지 않는 다른 클래스에 수락 테스트가 있으므로이 작업이 필요했습니다.
Kyanite

104

위에서 언급했듯이 모든 좋은 단위 테스트는 100 % 격리되어야합니다. 공유 상태 (예 : static각 테스트에 의해 수정 된 속성 에 따라 다름)를 사용하는 것은 나쁜 습관으로 간주됩니다.

즉, xUnit 테스트를 순서대로 실행하는 것에 대한 질문에 답이 있습니다! 내 시스템이 정적 서비스 로케이터 (이상적이지 않음)를 사용하기 때문에 정확히 동일한 문제가 발생했습니다.

기본적으로 xUnit 2.x는 모든 테스트를 병렬로 실행합니다. 이는 CollectionBehavior테스트 프로젝트의 AssemblyInfo.cs에서 정의하여 어셈블리별로 수정할 수 있습니다 .

어셈블리 별 분리 사용 :

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

또는 전혀 사용하지 않는 경우 :

[assembly: CollectionBehavior(DisableTestParallelization = true)]

후자는 아마도 당신이 원하는 것입니다. 병렬화 및 구성에 대한 자세한 정보는 xUnit 문서 에서 찾을 수 있습니다 .


5
저에게는 각 클래스 내의 메서드간에 공유 리소스가있었습니다. 한 클래스에서 다른 클래스에서 테스트를 실행하면 두 클래스의 테스트가 모두 중단됩니다. 을 사용하여 해결할 수있었습니다 [assembly: CollectionBehavior(CollectionBehavior.CollectionPerClass, DisableTestParallelization = true)]. @Squiggle 덕분에 모든 테스트를 실행하고 커피를 마실 수 있습니다! :)
Alielson Piffer

2
Abhinav Saxena의 답변은 .NET Core에 대해 더 세분화되어 있습니다.
Yennefer

65

.NET Core 프로젝트의 경우 다음을 사용 xunit.runner.json하여 만듭니다 .

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

또한, 당신은 csproj포함해야

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

이전 .Net Core 프로젝트의 경우 다음 project.json을 포함해야합니다.

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

2
최신 csproj dotnet core가 동등 <ItemGroup><None Include="xunit.runner.json" CopyToOutputDirectory="Always" /></ItemGroup>하거나 유사 하다고 가정 합니까?
Squiggle

3
이것은 csproj에서 나를 위해 일했습니다 :<ItemGroup> <None Update="xunit.runner.json"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </None> </ItemGroup>
skynyrd

병렬화 비활성화는 xUnit 이론에서 작동합니까?
John Zabroski

이것은 나를 위해 일한 유일한 것입니다. 나는 달리기를 시도했지만 dotnet test --no-build -c Release -- xunit.parallelizeTestCollections=false나를 위해 작동하지 않았습니다.
하비

18

.NET Core 프로젝트의 경우 https://xunit.github.io/docs/configuring-with-json.html에xunit.runner.json 설명 된대로 파일로 xUnit을 구성 할 수 있습니다. .

병렬 테스트 실행을 중지하기 위해 변경해야하는 설정은 parallelizeTestCollections입니다. 기본값은 true다음과 같습니다.

true어셈블리가이 어셈블리 내부에서 서로에 대해 병렬로 테스트를 실행하려는 경우이 값을 설정하십시오 . ... 다음으로 설정false 이 테스트 어셈블리 내의 모든 병렬화를 비활성화 .

JSON 스키마 유형 : 부울
기본값 :true

따라서이 xunit.runner.json목적을위한 최소값 은 다음과 같습니다.

{
    "parallelizeTestCollections": false
}

문서에서 언급했듯이 다음 방법 중 하나로이 파일을 빌드에 포함해야합니다.

  • Visual Studio 의 파일 속성이 최신 인 경우 출력 디렉터리복사를 복사로 설정 하거나
  • 첨가

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

    당신에 .csproj파일 또는

  • 첨가

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

    당신의 project.json파일에

프로젝트 유형에 따라.

마지막으로 위의 것 외에도 Visual Studio를 사용하는 경우 실수로 병렬로 테스트 실행 단추를 클릭하지 않았는지 확인합니다. 그러면 .NET 에서 병렬화를 해제 한 경우에도 테스트가 병렬로 실행됩니다 xunit.runner.json. Microsoft의 UI 디자이너는 실수로 클릭 할 가능성을 최대화하고 테스트 이유를 모를 가능성을 극대화하기 위해이 버튼에 레이블이 지정되지 않고 눈에 띄기 어렵게 만들고 테스트 탐색기 의 "모두 실행" 버튼에서 약 1cm 떨어져 있습니다. 갑자기 실패합니다.

원으로 표시된 버튼이있는 스크린 샷


@JohnZabroski 제안 된 편집 내용을 이해하지 못합니다 . ReSharper와 어떤 관계가 있습니까? 위의 답변을 작성할 때 설치했을 것 같지만 여기에있는 모든 것이 사용 여부와 무관하지 않습니까? 편집에서 링크 하는 페이지xunit.runner.json파일 지정과 어떤 관련이 있습니까? 그리고을 지정하는 것은 xunit.runner.json테스트를 직렬로 실행 하는 것과 관련이 있습니까?
Mark Amery

테스트를 순차적으로 실행하려고하는데, 처음에는 문제가 ReSharper와 관련이 있다고 생각했습니다 (ReSharper에는 Visual Studio 테스트 탐색기처럼 "병렬로 테스트 실행"버튼이 없기 때문에). 하지만 [이론]을 사용하면 테스트가 분리되지 않은 것 같습니다. 내가 읽은 모든 것이 Class가 가장 작은 병렬화 가능한 단위임을 암시하기 때문에 이것은 이상합니다.
John Zabroski

8

이것은 오래된 질문이지만 저처럼 새로 검색하는 사람들에게 해결책을 쓰고 싶었습니다. :)

참고 : xunit 버전 2.4.1과의 Dot Net Core WebUI 통합 테스트에서이 방법을 사용합니다.

NonParallelCollectionDefinitionClass라는 빈 클래스를 만든 다음 아래와 같이이 클래스에 CollectionDefinition 속성을 부여합니다. (중요한 부분은 DisableParallelization = true 설정입니다.)

using Xunit;

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

그런 다음 아래와 같이 병렬로 실행하지 않으려는 클래스에 Collection 속성을 추가하십시오. (중요한 부분은 컬렉션의 이름입니다. CollectionDefinition에서 사용하는 이름과 동일해야합니다.)

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

이렇게하면 먼저 다른 병렬 테스트가 실행됩니다. 그 후 Collection ( "Non-Parallel Collection") 속성이있는 다른 테스트가 실행됩니다.


6

당신은 재생 목록 을 사용할 수 있습니다

테스트 방법을 마우스 오른쪽 버튼으로 클릭-> 재생 목록에 추가-> 새 재생 목록

그런 다음 실행 순서를 지정할 수 있습니다. 기본값은 재생 목록에 추가 할 때이지만 원하는대로 재생 목록 파일을 변경할 수 있습니다.

여기에 이미지 설명 입력


5

세부 사항은 모르지만 단위 테스트 보다는 통합 테스트를 시도 하는 것 같습니다 . 에 대한 종속성을 분리 할 수 ​​있다면 테스트를 더 쉽고 빠르게 할 수 있습니다. 따라서 (예를 들어) 다음을 독립적으로 테스트 할 수 있습니다.ServiceHost

  • 구성 읽기 클래스
  • ServiceHost 팩토리 (통합 테스트로 가능)
  • IServiceHostFactory및 an 을 취하는 엔진 클래스IConfiguration

격리 (모의) 프레임 워크 및 (선택적으로) IoC 컨테이너 프레임 워크를 포함하는 데 도움이되는 도구입니다. 보다:


통합 테스트를 시도하는 것이 아닙니다. 실제로 단위 테스트를해야합니다. 나는 TDD / BDD 용어와 관행 (IoC, DI, Mocking 등)에 완전히 정통하기 때문에 공장을 만들고 인터페이스를 사용하는 것과 같은 공장 종류의 실행은 내가 필요로하는 것이 아닙니다 (이미 완료되었습니다. ServiceHost 자체의 경우는 예외입니다.) ServiceHost는 제대로 모의 할 수 없기 때문에 분리 될 수있는 종속성이 아닙니다 (많은 .NET 시스템 네임 스페이스만큼). 단위 테스트를 직렬로 실행할 방법이 정말 필요합니다.
jrista

1
@jrista-당신의 기술에 약간의 의도가 없었습니다. 저는 WCF 개발자가 아니지만 엔진이 래퍼의 인터페이스를 사용하여 ServiceHost 주변에 래퍼를 반환 할 수 있습니까? 아니면 ServiceHosts를위한 맞춤형 팩토리일까요?
TrueWill

호스팅 엔진은 ServiceHost를 반환하지 않습니다. 실제로 아무것도 반환하지 않으며 단순히 내부적으로 ServiceHost의 생성, 열기 및 닫기를 관리합니다. 모든 기본 WCF 유형을 래핑 할 수는 있지만 실제로 수행 할 권한이없는 많은 작업입니다. 또한이 문제는 병렬 실행으로 인한 것이 아니라 정상 작동 중에도 발생합니다. 나는 문제에 대해 여기에서 또 다른 질문을 시작했으며 희망적으로 대답을 얻을 수 있습니다.
jrista

@TrueWill : BTW, 나는 당신이 내 기술을 조금이라도 무시하는 것에 대해 걱정하지 않았습니다 ... 나는 단지 단위 테스트에 관한 모든 일반적인 것들을 다루는 많은 평범한 대답을 얻고 싶지 않았습니다. 매우 구체적인 문제에 대한 빠른 답변이 필요했습니다. 내가 조금 더럽혀 서 미안해, 내 의도가 아니었다. 이 작업을 수행하는 데 시간이 상당히 제한되어 있습니다.
jrista

3

Advanced Unit Testing을 사용할 수 있습니다 . 테스트를 실행하는 순서를 정의 할 수 있습니다 . 따라서 이러한 테스트를 호스팅하려면 새 cs 파일을 만들어야 할 수도 있습니다.

원하는 순서로 작동하도록 테스트 방법을 구부리는 방법은 다음과 같습니다.

[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.");
  ...
}

작동하는지 알려주세요.


훌륭한 기사. 실제로 CP에 북마크를 추가했습니다. 링크 주셔서 감사합니다.하지만 밝혀진대로 테스트 실행자가 테스트를 병렬로 실행하지 않는 것처럼 보이기 때문에 문제는 훨씬 더 깊은 것 같습니다.
jrista

2
잠깐, 먼저 테스트가 병렬로 실행되는 것을 원하지 않는다고 말한 다음 문제는 테스트 러너가 테스트를 병렬로 실행하지 않는다는 것입니다.
Graviton

제공 한 링크가 더 이상 작동하지 않습니다. 그리고 이것이 xunit으로 할 수있는 일입니까?
Allen Wang


0

기본 클래스에 [Collection ( "Sequential")] 속성을 추가했습니다 .

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


      public class TestClass1 : SequentialTest
      {
      ...
      }

      public class TestClass2 : SequentialTest
      {
      ...
      }
    }

0

지금까지 제안 된 답변 중 어느 것도 나를 위해 일하지 않았습니다. XUnit 2.4.1이 포함 된 dotnet 코어 앱이 있습니다. 대신 각 단위 테스트에 잠금을 설정하여 해결 방법으로 원하는 동작을 달성했습니다. 제 경우에는 실행 순서에 신경 쓰지 않고 테스트가 순차적이었습니다.

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

    [Fact]
    void Test2()
    {
        lock (this)
        {
            //Test Code
        }
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.