Enhetstestning med Orleans
Den här självstudien visar hur du enhetstestar dina korn för att se till att de fungerar korrekt. Det finns två huvudsakliga sätt att enhetstesta dina korn, och vilken metod du väljer beror på vilken typ av funktionalitet du testar. Microsoft .Orleans. TestingHost NuGet-paketet kan användas för att skapa testsilor för dina korn, eller så kan du använda ett hånfullt ramverk som Moq för att håna delar av körningen Orleans som ditt korn interagerar med.
Använd TestCluster
Microsoft.Orleans.TestingHost
NuGet-paketet innehåller TestCluster som kan användas för att skapa ett minnesinternt kluster, som består av två silor som standard, som kan användas för att testa korn.
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);
}
}
På grund av kostnaderna för att starta ett minnesinternt kluster kanske du vill skapa ett TestCluster
och återanvända det bland flera testfall. Detta kan till exempel göras med xUnits klass- eller samlingsfixturer.
Om du vill dela en TestCluster
mellan flera testfall skapar du först en typ av fixtur:
using Orleans.TestingHost;
public sealed class ClusterFixture : IDisposable
{
public TestCluster Cluster { get; } = new TestClusterBuilder().Build();
public ClusterFixture() => Cluster.Deploy();
void IDisposable.Dispose() => Cluster.StopAllSilos();
}
Skapa sedan en samlingsfixtur:
[CollectionDefinition(Name)]
public sealed class ClusterCollection : ICollectionFixture<ClusterFixture>
{
public const string Name = nameof(ClusterCollection);
}
Nu kan du återanvända en TestCluster
i dina testfall:
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 anropar Dispose() metoden av typen ClusterFixture
när alla tester har slutförts och de minnesinterna klustersilor stoppas. TestCluster
har också en konstruktor som accepterar TestClusterOptions som kan användas för att konfigurera silor i klustret.
Om du använder beroendeinmatning i din Silo för att göra tjänster tillgängliga för Grains kan du också använda det här mönstret:
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>(/* ... */);
});
}
}
Använda mocks
Orleans gör det också möjligt att håna många delar av systemet, och för många scenarier är detta det enklaste sättet att enhetstestkorn. Den här metoden har begränsningar (till exempel kring schemaläggning av återaktivering och serialisering) och kan kräva att korn innehåller kod som endast används av enhetstesterna. Orleans TestKit tillhandahåller en alternativ metod som gör många av dessa begränsningar åt sidan.
Anta till exempel att kornet du testar interagerar med andra korn. För att kunna håna de andra kornen måste du också håna GrainFactory medlemmen av kornet under testning. Som standard GrainFactory
är en normal protected
egenskap, men de flesta modelleringsramverk kräver att egenskaperna är public
och virtual
kan håna dem. Det första du behöver göra är att skapa GrainFactory
både en egenskap och virtual
en public
egenskap:
public new virtual IGrainFactory GrainFactory
{
get => base.GrainFactory;
}
Nu kan du skapa ditt korn utanför körningen Orleans och använda mocking för att styra beteendet GrainFactory
för :
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());
}
}
Här skapar du kornet under test, WorkerGrain
, med moq, vilket innebär att du kan åsidosätta beteendet GrainFactory
för så att det returnerar en hånad IJournalGrain
. Du kan sedan kontrollera att WorkerGrain
interagerar med det IJournalGrain
som du förväntar dig.