DEV Community

Pawel
Pawel

Posted on • Edited on

Robot framework

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.

Enter fullscreen mode Exit fullscreen mode

Top comments (0)