Compartilhar via


Teste de unidade com Orleans

Este tutorial mostra como fazer o teste de unidade dos grãos para garantir que eles se comportem corretamente. Há duas maneiras principais de testar os grãos por unidade e o método escolhido depende do tipo de funcionalidade que você está testando. Use o pacote NuGet Microsoft.Orleans.TestingHost para criar silos de teste para seus grãos, ou use um framework de simulação como o Moq para simular partes do runtime com o qual seu grão interage.

Usar o TestCluster

O Microsoft.Orleans.TestingHost pacote NuGet contém TestCluster, que você pode usar para criar um cluster na memória (composto por dois silos por padrão) para testar grãos.

using Orleans.TestingHost;

namespace Tests;

public class HelloGrainTests
{
    [Fact]
    public async Task SaysHelloCorrectly()
    {
        var builder = new TestClusterBuilder();
        var cluster = builder.Build();
        cluster.Deploy();

        var hello = cluster.GrainFactory.GetGrain<IHelloGrain>(Guid.NewGuid());
        var greeting = await hello.SayHello("World");

        cluster.StopAllSilos();

        Assert.Equal("Hello, World!", greeting);
    }
}

Devido à sobrecarga de iniciar um cluster na memória, talvez você queira criar um TestCluster e reutilizá-lo entre vários casos de teste. Por exemplo, obtenha isso usando as instalações de classe ou coleção do xUnit.

Para compartilhar um TestCluster entre vários casos de teste, primeiro crie uma fixura:

using Orleans.TestingHost;

public sealed class ClusterFixture : IDisposable
{
    public TestCluster Cluster { get; } = new TestClusterBuilder().Build();

    public ClusterFixture() => Cluster.Deploy();

    void IDisposable.Dispose() => Cluster.StopAllSilos();
}

Em seguida, crie uma instalação de coleção:

[CollectionDefinition(Name)]
public sealed class ClusterCollection : ICollectionFixture<ClusterFixture>
{
    public const string Name = nameof(ClusterCollection);
}

Agora você pode reutilizar um TestCluster em seus casos de teste:

using Orleans.TestingHost;

namespace Tests;

[Collection(ClusterCollection.Name)]
public class HelloGrainTestsWithFixture(ClusterFixture fixture)
{
    private readonly TestCluster _cluster = fixture.Cluster;

    [Fact]
    public async Task SaysHelloCorrectly()
    {
        var hello = _cluster.GrainFactory.GetGrain<IHelloGrain>(Guid.NewGuid());
        var greeting = await hello.SayHello("World");

        Assert.Equal("Hello, World!", greeting);
    }
}

Quando todos os testes são concluídos e os silos do cluster na memória param, o xUnit chama o método Dispose() do tipo ClusterFixture. TestCluster também tem um construtor que aceita TestClusterOptions que você pode usar para configurar os silos no grupo.

Se você usar a Injeção de Dependência em seu Silo para disponibilizar serviços ao Grains, também poderá usar esse padrão:

using Microsoft.Extensions.DependencyInjection;
using Orleans.TestingHost;

namespace Tests;

public sealed class ClusterFixtureWithConfig : IDisposable
{
    public TestCluster Cluster { get; } = new TestClusterBuilder()
        .AddSiloBuilderConfigurator<TestSiloConfigurations>()
        .Build();

    public ClusterFixtureWithConfig() => Cluster.Deploy();

    void IDisposable.Dispose() => Cluster.StopAllSilos();
}

file sealed class TestSiloConfigurations : ISiloConfigurator
{
    public void Configure(ISiloBuilder siloBuilder)
    {
        siloBuilder.ConfigureServices(static services =>
        {
            // TODO: Call required service registrations here.
            // services.AddSingleton<T, Impl>(/* ... */);
        });
    }
}

Usar simulações

Orleans também permite zombar de muitas partes do sistema. Para muitos cenários, essa é a maneira mais fácil de unitar os grãos de teste. Essa abordagem tem limitações (por exemplo, em relação ao agendamento de reentrância e serialização) e pode exigir grãos para incluir o código usado apenas pelos testes de unidade. O Orleans TestKit fornece uma abordagem alternativa que ignora muitas dessas limitações.

Por exemplo, imagine que o grão que você está testando interage com outros grãos. Para zombar desses outros grãos, você também precisa zombar do GrainFactory membro do grão em teste. Por padrão, GrainFactory é uma propriedade normal protected, mas a maioria dos frameworks de simulação exige que as propriedades sejam public e virtual para habilitar a simulação. Portanto, a primeira etapa é fazer GrainFactory tanto public quanto virtual:

public new virtual IGrainFactory GrainFactory
{
    get => base.GrainFactory;
}

Agora você pode criar seus grãos fora do Orleans runtime e usar a zombaria para controlar o comportamento de GrainFactory:

using Xunit;
using Moq;

namespace Tests;

public class WorkerGrainTests
{
    [Fact]
    public async Task RecordsMessageInJournal()
    {
        var data = "Hello, World";
        var journal = new Mock<IJournalGrain>();
        var worker = new Mock<WorkerGrain>();
        worker
            .Setup(x => x.GrainFactory.GetGrain<IJournalGrain>(It.IsAny<Guid>()))
            .Returns(journal.Object);

        await worker.DoWork(data)

        journal.Verify(x => x.Record(data), Times.Once());
    }
}

Aqui, crie o grão em teste, WorkerGrainusando Moq. Isso permite substituir o comportamento do GrainFactory para que ele retorne um IJournalGrain simulado. Em seguida, você pode verificar se WorkerGrain interage com IJournalGrain conforme o esperado.