Partilhar via


Testes unitários com Orleans

Este tutorial mostra como testar seus grãos para garantir que eles se comportem corretamente. Há duas maneiras principais de testar seus grãos por unidade, e o método escolhido dependerá do tipo de funcionalidade que você está testando. A Microsoft.Orleans. O pacote TestingHost NuGet pode ser usado para criar silos de teste para seus grãos, ou você pode usar uma estrutura simulada como o Moq para simular partes do tempo de execução com as Orleans quais seu grão interage.

Utilize o TestCluster

O Microsoft.Orleans.TestingHost pacote NuGet contém TestCluster o que pode ser usado para criar um cluster na memória, composto por dois silos por padrão, que pode ser usado 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, você pode querer criar um TestCluster e reutilizá-lo entre vários casos de teste. Por exemplo, isso pode ser feito usando a classe xUnit ou equipamentos de coleção.

Para compartilhar um TestCluster entre vários casos de teste, primeiro crie um tipo de fixture:

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 um acessório 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);
    }
}

xUnit chama o Dispose() ClusterFixture método do tipo quando todos os testes foram concluídos e os silos de cluster na memória são interrompidos. TestCluster também tem um construtor que aceita TestClusterOptions que pode ser usado para configurar os silos no cluster.

Se você estiver usando a injeção de dependência em seu silo para disponibilizar serviços para grãos, você também pode usar este 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>(/* ... */);
        });
    }
}

Use simulações

Orleans também torna possível simular muitas partes do sistema e, para muitos cenários, esta é a maneira mais fácil de testar grãos por unidade. Essa abordagem tem limitações (por exemplo, em torno do agendamento de reentrância e serialização) e pode exigir que os grãos incluam código usado apenas por seus testes de unidade. O Orleans TestKit fornece uma abordagem alternativa, que contorna muitas dessas limitações.

Por exemplo, imagine que o grão que você está testando interage com outros grãos. Para ser capaz de 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 das estruturas simuladas requer que as propriedades sejam public e virtual sejam capazes de zombar delas. Então, a primeira coisa que você precisa fazer é fazer GrainFactory um public e virtual propriedade:

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

Agora você pode criar seu grão fora do Orleans tempo de execução e usar o mocking 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 você cria o grão em teste, WorkerGrainusando Moq, o GrainFactory que significa que você pode substituir o comportamento do para que ele retorne um simulado IJournalGrain. Em seguida, você pode verificar se o WorkerGrain interage com o IJournalGrain esperado.