Tak, można osiągnąć podobny efekt bez konieczności stosowania niestandardowego atrybutu [CustomFact]. Możemy użyć mechanizmu xUnit do rejestrowania własnych „test case discoverers”, co pozwoli automatycznie wykonywać metody oznaczone [BeforeEach] i [AfterEach] przed i po każdym teście bez zmieniania standardowych [Fact] i [Theory].
Implementacja
1. Klasa BeforeAfterTestCase:
Stwórz własną implementację klasy XunitTestCase, która obsłuży metody [BeforeEach] i [AfterEach].
csharp
Copy code
using System;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Xunit.Abstractions;
using Xunit.Sdk;
public class BeforeAfterTestCase : XunitTestCase
{
public BeforeAfterTestCase(
IMessageSink diagnosticMessageSink,
TestMethodDisplay defaultMethodDisplay,
TestMethodDisplayOptions defaultMethodDisplayOptions,
ITestMethod testMethod,
object[] testMethodArguments = null)
: base(diagnosticMessageSink, defaultMethodDisplay, defaultMethodDisplayOptions, testMethod, testMethodArguments)
{
}
public override async Task<RunSummary> RunAsync(
IMessageSink diagnosticMessageSink,
IMessageBus messageBus,
object[] constructorArguments,
ExceptionAggregator aggregator,
CancellationTokenSource cancellationTokenSource)
{
var testClassInstance = constructorArguments.FirstOrDefault();
if (testClassInstance != null)
{
// Znajdź metody oznaczone [BeforeEach]
var beforeMethods = testClassInstance.GetType()
.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.Where(m => m.GetCustomAttribute<BeforeEachAttribute>() != null);
// Znajdź metody oznaczone [AfterEach]
var afterMethods = testClassInstance.GetType()
.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.Where(m => m.GetCustomAttribute<AfterEachAttribute>() != null);
try
{
// Wykonaj [BeforeEach]
foreach (var beforeMethod in beforeMethods)
{
beforeMethod.Invoke(testClassInstance, null);
}
// Wykonaj test
var runSummary = await base.RunAsync(diagnosticMessageSink, messageBus, constructorArguments, aggregator, cancellationTokenSource);
return runSummary;
}
finally
{
// Wykonaj [AfterEach]
foreach (var afterMethod in afterMethods)
{
afterMethod.Invoke(testClassInstance, null);
}
}
}
return await base.RunAsync(diagnosticMessageSink, messageBus, constructorArguments, aggregator, cancellationTokenSource);
}
}
2. Odkrywacz testów BeforeAfterTestCaseDiscoverer:
Zarejestruj własny odkrywacz testów, który będzie używał klasy BeforeAfterTestCase.
csharp
Copy code
using System;
using Xunit.Abstractions;
using Xunit.Sdk;
public class BeforeAfterTestCaseDiscoverer : IXunitTestCaseDiscoverer
{
private readonly IMessageSink _diagnosticMessageSink;
public BeforeAfterTestCaseDiscoverer(IMessageSink diagnosticMessageSink)
{
_diagnosticMessageSink = diagnosticMessageSink;
}
public IEnumerable<IXunitTestCase> Discover(
ITestFrameworkDiscoveryOptions discoveryOptions,
ITestMethod testMethod,
IAttributeInfo factAttribute)
{
yield return new BeforeAfterTestCase(
_diagnosticMessageSink,
discoveryOptions.MethodDisplayOrDefault(),
discoveryOptions.MethodDisplayOptionsOrDefault(),
testMethod);
}
}
3. Powiąż odkrywacz z Fact i Theory:
W pliku konfiguracyjnym projektu AssemblyInfo.cs wskaż nowy odkrywacz testów:
csharp
Copy code
[assembly: Xunit.TestCaseDiscoverer("Namespace.BeforeAfterTestCaseDiscoverer", "YourAssemblyName")]
Zastąp Namespace nazwą przestrzeni nazw, a YourAssemblyName nazwą projektu.
4. Atrybuty [BeforeEach] i [AfterEach]:
Definicja atrybutów pozostaje taka sama:
csharp
Copy code
using System;
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class BeforeEachAttribute : Attribute { }
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class AfterEachAttribute : Attribute { }
5. Przykład użycia:
Testy pozostają standardowe, bez potrzeby używania CustomFact.
csharp
Copy code
using System;
using Xunit;
public class MyTests
{
[BeforeEach]
public void Setup()
{
Console.WriteLine("Running setup before each test...");
}
[AfterEach]
public void Cleanup()
{
Console.WriteLine("Running cleanup after each test...");
}
[Fact]
public void Test1()
{
Console.WriteLine("Executing Test1");
Assert.True(true);
}
[Fact]
public void Test2()
{
Console.WriteLine("Executing Test2");
Assert.True(true);
}
}
Przykładowy wynik działania:
sql
Copy code
Running setup before each test...
Executing Test1
Running cleanup after each test...
Running setup before each test...
Executing Test2
Running cleanup after each test...
Wyjaśnienie:
Automatyczne działanie z [Fact] i [Theory]:
Dzięki modyfikacji systemu odkrywania testów, atrybuty [Fact] i [Theory] automatycznie używają klasy BeforeAfterTestCase.
Mechanizm refleksji:
Metody oznaczone [BeforeEach] i [AfterEach] są wykrywane za pomocą refleksji i wykonywane przed oraz po każdym teście.
Brak konieczności modyfikacji testów:
Testy korzystają z domyślnych [Fact] i [Theory], co upraszcza ich implementację i pozwala na łatwą integrację z istniejącymi testami.
For further actions, you may consider blocking this person and/or reporting abuse
Top comments (0)