TL; DR-MSTest의 xUnit에 해당하는 기능을 찾고 있습니다 AssemblyInitialize
(내가 좋아하는 하나의 기능).
특히 다른 종속성없이 실행할 수있는 Selenium 연기 테스트가 있기 때문에 찾고 있습니다. 나는 나를 위해 IisExpress를 시작하고 처분 할 때 죽일 Fixture가 있습니다. 그러나 모든 테스트 전에 이것을 수행하면 런타임이 엄청나게 부풀어집니다.
테스트를 시작할 때이 코드를 한 번 트리거하고 마지막에 폐기 (프로세스 종료)하고 싶습니다. 어떻게하면 되나요?
“현재 실행중인 테스트 수”와 같은 것에 프로그래밍 방식으로 액세스 할 수 있어도 뭔가 알아낼 수 있습니다.
답변
2015 년 11 월부터 xUnit 2가 출시되었으므로 테스트간에 기능을 공유하는 표준 방법이 있습니다. 여기에 문서화되어 있습니다 .
기본적으로 픽스쳐를 수행하는 클래스를 만들어야합니다.
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
속성이 있는 더미 클래스 입니다. 이 클래스는 Xunit이 테스트 컬렉션을 생성 할 수 있도록하며 컬렉션의 모든 테스트 클래스에 대해 주어진 픽스처를 사용할 것입니다.
[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;
}
}
AssemblyInitialize
어떤 테스트 컬렉션이 속하는지 각 테스트 클래스에 선언해야하기 때문에 MsTests보다 조금 더 장황 하지만 모듈화도 더 가능합니다.
참고 : 샘플은 설명서 에서 가져 왔습니다 .
답변
정적 필드를 만들고 종료자를 구현합니다.
xUnit이 AppDomain을 만들어 테스트 어셈블리를 실행하고 완료되면 언로드한다는 사실을 사용할 수 있습니다. 앱 도메인을 언로드하면 종료자가 실행됩니다.
이 방법을 사용하여 IISExpress를 시작하고 중지합니다.
public sealed class ExampleFixture
{
public static ExampleFixture Current = new ExampleFixture();
private ExampleFixture()
{
// Run at start
}
~ExampleFixture()
{
Dispose();
}
public void Dispose()
{
GC.SuppressFinalize(this);
// Run at end
}
}
편집 : ExampleFixture.Current
테스트에서 사용하여 조명기에 액세스합니다 .
답변
어셈블리 초기화에서 코드를 실행하려면 다음을 수행 할 수 있습니다 (xUnit 2.3.1로 테스트 됨).
using Xunit.Abstractions;
using Xunit.Sdk;
[assembly: Xunit.TestFramework("MyNamespace.MyClassName", "MyAssemblyName")]
namespace MyNamespace
{
public class MyClassName : XunitTestFramework
{
public MyClassName(IMessageSink messageSink)
:base(messageSink)
{
// Place initialization code here
}
public new void Dispose()
{
// Place tear down code here
base.Dispose();
}
}
}
https://github.com/xunit/samples.xunit/tree/master/AssemblyFixtureExample 도 참조하십시오.
답변
오늘날 프레임 워크에서는 불가능합니다. 이것은 2.0에 계획된 기능입니다.
2.0 이전에이 작업을 수행하려면 프레임 워크에서 중요한 재 아키텍처를 수행하거나 고유 한 특성을 인식하는 자체 러너를 작성해야합니다.
답변
내가 사용 AssemblyFixture ( NuGet을 ).
그것이하는 일은 테스트 어셈블리로서 개체의 수명을 원하는 곳을 IAssemblyFixture<T>
대체 하는 인터페이스를 제공 한다는 것입니다 IClassFixture<T>
.
예:
public class Singleton { }
public class TestClass1 : IAssemblyFixture<Singleton>
{
readonly Singletone _Singletone;
public TestClass1(Singleton singleton)
{
_Singleton = singleton;
}
[Fact]
public void Test1()
{
//use singleton
}
}
public class TestClass2 : IAssemblyFixture<Singleton>
{
readonly Singletone _Singletone;
public TestClass2(Singleton singleton)
{
//same singleton instance of TestClass1
_Singleton = singleton;
}
[Fact]
public void Test2()
{
//use singleton
}
}
답변
모든 xUnit 테스트가 끝날 때 실행할 수있는 옵션이 없어서 상당히 짜증이났습니다. 여기에있는 일부 옵션은 모든 테스트를 변경하거나 하나의 컬렉션에 넣는 (동 기적으로 실행됨을 의미) 포함하기 때문에 그다지 좋지 않습니다. 그러나 Rolf Kristensen의 대답은이 코드를 얻는 데 필요한 정보를 제공했습니다. 약간 길지만 테스트 프로젝트에 추가하기 만하면되고 다른 코드 변경은 필요하지 않습니다.
using Siderite.Tests;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using Xunit;
using Xunit.Abstractions;
using Xunit.Sdk;
[assembly: TestFramework(
SideriteTestFramework.TypeName,
SideriteTestFramework.AssemblyName)]
namespace Siderite.Tests
{
public class SideriteTestFramework : ITestFramework
{
public const string TypeName = "Siderite.Tests.SideriteTestFramework";
public const string AssemblyName = "Siderite.Tests";
private readonly XunitTestFramework _innerFramework;
public SideriteTestFramework(IMessageSink messageSink)
{
_innerFramework = new XunitTestFramework(messageSink);
}
public ISourceInformationProvider SourceInformationProvider
{
set
{
_innerFramework.SourceInformationProvider = value;
}
}
public void Dispose()
{
_innerFramework.Dispose();
}
public ITestFrameworkDiscoverer GetDiscoverer(IAssemblyInfo assembly)
{
return _innerFramework.GetDiscoverer(assembly);
}
public ITestFrameworkExecutor GetExecutor(AssemblyName assemblyName)
{
var executor = _innerFramework.GetExecutor(assemblyName);
return new SideriteTestExecutor(executor);
}
private class SideriteTestExecutor : ITestFrameworkExecutor
{
private readonly ITestFrameworkExecutor _executor;
private IEnumerable<ITestCase> _testCases;
public SideriteTestExecutor(ITestFrameworkExecutor executor)
{
this._executor = executor;
}
public ITestCase Deserialize(string value)
{
return _executor.Deserialize(value);
}
public void Dispose()
{
_executor.Dispose();
}
public void RunAll(IMessageSink executionMessageSink, ITestFrameworkDiscoveryOptions discoveryOptions, ITestFrameworkExecutionOptions executionOptions)
{
_executor.RunAll(executionMessageSink, discoveryOptions, executionOptions);
}
public void RunTests(IEnumerable<ITestCase> testCases, IMessageSink executionMessageSink, ITestFrameworkExecutionOptions executionOptions)
{
_testCases = testCases;
_executor.RunTests(testCases, new SpySink(executionMessageSink, this), executionOptions);
}
internal void Finished(TestAssemblyFinished executionFinished)
{
// do something with the run test cases in _testcases and the number of failed and skipped tests in executionFinished
}
}
private class SpySink : IMessageSink
{
private readonly IMessageSink _executionMessageSink;
private readonly SideriteTestExecutor _testExecutor;
public SpySink(IMessageSink executionMessageSink, SideriteTestExecutor testExecutor)
{
this._executionMessageSink = executionMessageSink;
_testExecutor = testExecutor;
}
public bool OnMessage(IMessageSinkMessage message)
{
var result = _executionMessageSink.OnMessage(message);
if (message is TestAssemblyFinished executionFinished)
{
_testExecutor.Finished(executionFinished);
}
return result;
}
}
}
}
하이라이트 :
- 어셈블리 : TestFramework는 xUnit에 기본 프레임 워크를 프록시하는 프레임 워크를 사용하도록 지시합니다.
- SideriteTestFramework는 또한 실행기를 사용자 지정 클래스로 래핑 한 다음 메시지 싱크를 래핑합니다.
- 결국 Finished 메서드가 실행되고 테스트 목록이 실행되고 xUnit 메시지의 결과가 표시됩니다.
여기서 더 많은 작업을 수행 할 수 있습니다. 테스트 실행에 신경 쓰지 않고 작업을 실행하려면 XunitTestFramework에서 상속하고 메시지 싱크를 래핑하면됩니다.
답변
IUseFixture 인터페이스를 사용하여이를 수행 할 수 있습니다. 또한 모든 테스트는 TestBase 클래스를 상속해야합니다. 테스트에서 직접 OneTimeFixture를 사용할 수도 있습니다.
public class TestBase : IUseFixture<OneTimeFixture<ApplicationFixture>>
{
protected ApplicationFixture Application;
public void SetFixture(OneTimeFixture<ApplicationFixture> data)
{
this.Application = data.Fixture;
}
}
public class ApplicationFixture : IDisposable
{
public ApplicationFixture()
{
// This code run only one time
}
public void Dispose()
{
// Here is run only one time too
}
}
public class OneTimeFixture<TFixture> where TFixture : new()
{
// This value does not share between each generic type
private static readonly TFixture sharedFixture;
static OneTimeFixture()
{
// Constructor will call one time for each generic type
sharedFixture = new TFixture();
var disposable = sharedFixture as IDisposable;
if (disposable != null)
{
AppDomain.CurrentDomain.DomainUnload += (sender, args) => disposable.Dispose();
}
}
public OneTimeFixture()
{
this.Fixture = sharedFixture;
}
public TFixture Fixture { get; private set; }
}
편집 : 새 조명기가 각 테스트 클래스에 대해 생성하는 문제를 수정합니다.