Tests unitaires avec Orleans
Ce tutoriel montre comment tester vos grains de façon unitaire pour vous assurer qu’ils se comportent correctement. Il existe deux méthodes principales pour tester vos grains de façon unitaire. La méthode que vous choisissez dépend du type de fonctionnalité que vous testez. Le package NuGet Microsoft.Orleans.TestingHost peut être utilisé pour créer des silos de test pour vos grains. Vous pouvez également utiliser une infrastructure de simulation comme Moq pour simuler les parties du runtime Orleans avec lesquelles votre grain interagit.
Utilisation de la TestCluster
.
Le package NuGet Microsoft.Orleans.TestingHost
contient TestCluster, qui permet de créer un cluster en mémoire, composé de deux silos par défaut, qui peuvent être utilisés pour tester les grains.
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);
}
}
En raison de la surcharge liée au démarrage d’un cluster en mémoire, vous pouvez créer un TestCluster
et le réutiliser parmi plusieurs cas de test. Par exemple, cela peut être effectué à l’aide des fixtures de classe ou de collection de xUnit.
Pour partager un TestCluster
entre plusieurs cas de test, commencez par créer un type 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();
}
Ensuite, créez une fixture de collection :
[CollectionDefinition(Name)]
public sealed class ClusterCollection : ICollectionFixture<ClusterFixture>
{
public const string Name = nameof(ClusterCollection);
}
Vous pouvez maintenant réutiliser un TestCluster
dans vos cas de test :
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 appelle la méthode Dispose() du type ClusterFixture
quand tous les tests sont effectués et que les silos du cluster en mémoire sont arrêtés. TestCluster
a également un constructeur qui accepte les TestClusterOptions qui peuvent être utilisées pour configurer les silos dans le cluster.
Si vous utilisez l’injection de dépendances dans votre silo pour rendre les services disponibles aux grains, vous pouvez également utiliser le modèle suivant :
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>(/* ... */);
});
}
}
Utiliser des simulations
Orleans permet également de simuler de nombreux composants du système, et pour de nombreux scénarios, il s’agit du moyen le plus simple de tester les grains de façon unitaire. Cette approche présente des limites (par exemple, concernant la planification de la réentrance et de la sérialisation) et peut nécessiter que les grains incluent du code utilisé uniquement par vos tests unitaires. Le kit de test Orleans propose une approche alternative qui permet de contourner beaucoup de ces limites.
Par exemple, supposez que le grain que vous testez interagisse avec d’autres grains. Pour pouvoir simuler ces autres grains, vous devez également simuler le membre GrainFactory du grain testé. Par défaut, GrainFactory
est une propriété protected
normale, mais la plupart des infrastructures de simulation nécessitent des propriétés public
et virtual
pour pouvoir les simuler. Ainsi, la première action à entreprendre consiste à faire de GrainFactory
une propriété à la fois public
et virtual
:
public new virtual IGrainFactory GrainFactory
{
get => base.GrainFactory;
}
Vous pouvez désormais créer votre grain en dehors du runtime Orleans et utiliser la simulation pour contrôler le comportement 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());
}
}
Ici, vous créez votre grain en cours de test, WorkerGrain
, à l’aide de Moq. Cela signifie que vous pouvez ensuite remplacer le comportement de GrainFactory
afin qu’il retourne un élément IJournalGrain
simulé. Vous pouvez ensuite vérifier que le WorkerGrain
interagit avec le IJournalGrain
comme prévu.