[c#] xUnit.net : 글로벌 설정 + 분해?

이 질문은 단위 테스트 프레임 워크 xUnit.net에 관한 것 입니다.

테스트가 실행되기 전에 일부 코드를 실행해야하고 모든 테스트가 완료된 후 일부 코드도 실행해야합니다. 전역 초기화 및 종료 코드를 나타내는 일종의 속성 또는 마커 인터페이스가 있어야한다고 생각했지만 찾을 수 없었습니다.

또는 프로그래밍 방식으로 xUnit을 호출하면 다음 코드를 사용하여 원하는 것을 얻을 수도 있습니다.

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

누구나 전역 설정 / 해체 코드를 선언적으로 또는 프로그래밍 방식으로 실행하는 방법에 대한 힌트를 제공 할 수 있습니까?



답변

내가 아는 한 xUnit에는 전역 초기화 / 해체 확장 점이 없습니다. 그러나 쉽게 만들 수 있습니다. IDisposable생성자에서 초기화 를 구현 하고 수행 하는 기본 테스트 클래스를 만들고 IDisposable.Dispose메서드 에서 분해를 수행하면 됩니다. 이것은 다음과 같습니다.

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
}

그러나 기본 클래스 설정 및 해제 코드는 각 호출에 대해 실행됩니다. 이것은 매우 효율적이지 않기 때문에 원하는 것이 아닐 수도 있습니다. 더 최적화 된 버전은 IClassFixture<T>인터페이스를 사용하여 전역 초기화 / 해체 기능이 한 번만 호출되도록합니다. 이 버전의 경우 테스트 클래스에서 기본 클래스를 확장하지 않고 조명기 클래스를 참조 하는 IClassFixture<T>인터페이스를 구현합니다 T.

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)
    {
    }
}

의 생성자가 발생할 TestsFixture경우에만 테스트중인 모든 클래스에 대해 한 번 실행된다. 따라서 두 가지 방법 중에서 정확히 선택하려는 항목에 따라 다릅니다.


답변

나는 같은 대답을 찾고 있었고, 현재 xUnit 문서는 클래스 또는 클래스 그룹 수준에서 개발자에게 광범위한 설정 / 해체 기능을 제공하는 클래스 고정 및 컬렉션 고정을 구현하는 방법과 관련하여 매우 유용합니다. 이것은 Geir Sagberg의 답변과 일치하며 어떻게 생겼는지 설명하는 훌륭한 골격 구현을 제공합니다.

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

컬렉션 픽스처 사용시기 : 단일 테스트 컨텍스트를 생성하여 여러 테스트 클래스의 테스트간에 공유하고 테스트 클래스의 모든 테스트가 완료된 후 정리하도록 할 때.

때로는 여러 테스트 클래스간에 조명기 객체를 공유하고 싶을 것입니다. 클래스 픽스처에 사용 된 데이터베이스 예제는 좋은 예입니다. 테스트 데이터 세트로 데이터베이스를 초기화 한 다음 여러 테스트 클래스에서 사용할 수 있도록 해당 테스트 데이터를 그대로 둘 수 있습니다. xUnit.net의 컬렉션 픽스처 기능을 사용하여 여러 테스트 클래스의 테스트간에 단일 개체 인스턴스를 공유 할 수 있습니다.

수집 설비를 사용하려면 다음 단계를 수행해야합니다.

조명기 클래스를 만들고 조명기 클래스 생성자에 시작 코드를 넣습니다. 조명기 클래스가 정리를 수행해야하는 경우 조명기 클래스에서 IDisposable을 구현하고 Dispose () 메서드에 정리 코드를 넣습니다. 컬렉션 정의 클래스를 만들고 [CollectionDefinition] 속성으로 장식하고 테스트 컬렉션을 식별 할 고유 한 이름을 지정합니다. 컬렉션 정의 클래스에 ICollectionFixture <>를 추가합니다. 테스트 컬렉션 정의 클래스의 [CollectionDefinition] 특성에 제공 한 고유 이름을 사용하여 컬렉션의 일부가 될 모든 테스트 클래스에 [Collection] 특성을 추가합니다. 테스트 클래스가 픽스처 인스턴스에 액세스해야하는 경우 생성자 인수로 추가하면 자동으로 제공됩니다. 다음은 간단한 예입니다.

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은 컬렉션 픽스처 객체의 수명이 더 길다는 점을 제외하고는 클래스 픽스처와 거의 동일한 방식으로 컬렉션 픽스처를 취급합니다. 컬렉션의 테스트 클래스에서 테스트가 실행되기 전에 생성되며 정리되지 않습니다. 컬렉션의 모든 테스트 클래스 실행이 완료 될 때까지

테스트 컬렉션은 IClassFixture <>로 장식 할 수도 있습니다. xUnit.net은이를 테스트 컬렉션의 각 개별 테스트 클래스가 클래스 픽스처로 장식 된 것처럼 취급합니다.

테스트 컬렉션은 병렬로 실행할 때 xUnit.net이 테스트를 실행하는 방식에도 영향을줍니다. 자세한 내용은 병렬 테스트 실행을 참조하십시오.

중요 참고 사항 : Fixture는이를 사용하는 테스트와 동일한 어셈블리에 있어야합니다.


답변

쉬운 해결책이 있습니다. Fody.ModuleInit 플러그인 사용

https://github.com/Fody/ModuleInit

그것은 너겟 패키지이며 설치 ModuleInitializer.cs하면 프로젝트에 호출되는 새 파일을 추가합니다 . 여기에는 빌드 후 어셈블리로 짜여지고 어셈블리가로드 되 자마자 어떤 것이 실행되기 전에 실행되는 정적 메서드가 하나 있습니다.

구입 한 라이브러리에 대한 소프트웨어 라이센스를 잠금 해제하는 데 사용합니다. 나는 항상 각 테스트에서 라이센스를 잠금 해제하는 것을 잊고 있었고 심지어 잠금을 해제 할 기본 클래스에서 테스트를 파생시키는 것도 잊었습니다. 이 라이브러리를 작성한 밝은 불꽃은 라이센스가 잠겨 있다는 것을 알리는 대신 미묘한 숫자 오류를 일으켜 테스트가 실패하거나 통과하지 않아야 할 때 통과합니다. 라이브러리를 올바르게 잠금 해제했는지 여부는 알 수 없습니다. 이제 내 모듈 초기화는 다음과 같습니다.

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

이 어셈블리에 배치 된 모든 테스트에는 라이선스가 올바르게 잠금 해제됩니다.


답변

여러 클래스간에 SetUp / TearDown 코드를 공유하려면 xUnit의 CollectionFixture를 사용할 수 있습니다 .

인용문:

수집 설비를 사용하려면 다음 단계를 수행해야합니다.

  • 조명기 클래스를 만들고 조명기 클래스 생성자에 시작 코드를 넣습니다.
  • 조명기 클래스가 정리를 수행해야하는 경우 조명기 클래스에서 IDisposable을 구현하고 Dispose () 메서드에 정리 코드를 넣습니다.
  • 컬렉션 정의 클래스를 만들고 [CollectionDefinition] 속성으로 장식하고 테스트 컬렉션을 식별 할 고유 한 이름을 지정합니다.
  • 컬렉션 정의 클래스에 ICollectionFixture <>를 추가합니다.
  • 테스트 컬렉션 정의 클래스의 [CollectionDefinition] 특성에 제공 한 고유 이름을 사용하여 컬렉션의 일부가 될 모든 테스트 클래스에 [Collection] 특성을 추가합니다.
  • 테스트 클래스가 픽스처 인스턴스에 액세스해야하는 경우 생성자 인수로 추가하면 자동으로 제공됩니다.

답변