Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
In diesem Tutorial wird gezeigt, wie Sie Ihre Grains testen, um sicherzustellen, dass sie sich korrekt verhalten. Es gibt zwei Hauptmethoden, um Ihre Getreide zu testen, und die methode, die Sie auswählen, hängt von der Art der Funktionalität ab, die Sie testen. Verwenden Sie das Microsoft.Orleans.TestingHost NuGet-Paket, um Testsilos für Ihre Grains zu erstellen, oder verwenden Sie ein Mock-Framework wie Moq, um Teile der Orleans Laufzeitumgebung zu mocken, mit der Ihre Grains interagieren.
Verwenden Sie das InProcessTestCluster (empfohlen)
Dies InProcessTestCluster ist die empfohlene Testinfrastruktur für Orleans. Es stellt eine optimierte, delegierte API zum Konfigurieren von Testclustern bereit, wodurch die Freigabe von Diensten zwischen Ihren Tests und dem Cluster vereinfacht wird.
Wichtige Vorteile
Der hauptvorteil von InProcessTestCluster over TestCluster ist Ergonomie:
- Stellvertretungsbasierte Konfiguration: Silos und Clients mithilfe von Inline-Delegates anstelle von separaten Konfigurationsklassen konfigurieren.
- Instanzen gemeinsam genutzter Dienste: Einfaches Freigeben von Pseudodiensten, Testen von Doubles und anderen Instanzen zwischen Ihrem Testcode und den Silohosts
-
Weniger Bausteine: Es ist nicht erforderlich, separate
ISiloConfiguratorOderIClientConfiguratorKlassen zu erstellen - Einfachere Dependency-Injection: Dienste direkt in der Fluent API des Builders registrieren
Beide und InProcessTestClusterTestCluster verwenden den gleichen zugrunde liegenden In-Process-Silohost standardmäßig, sodass die Speicherauslastung und Startzeit gleichwertig sind. Die TestCluster API ist auch für die Unterstützung von Multiprozessszenarien (für produktionsähnliche Simulationen) konzipiert, die den klassenbasierten Konfigurationsansatz erfordert, aber standardmäßig wie InProcessTestClusterim Prozess ausgeführt wird.
Grundlegende Nutzung
using Orleans.TestingHost;
using Xunit;
public class HelloGrainTests : IAsyncLifetime
{
private InProcessTestCluster _cluster = null!;
public async Task InitializeAsync()
{
var builder = new InProcessTestClusterBuilder();
_cluster = builder.Build();
await _cluster.DeployAsync();
}
public async Task DisposeAsync()
{
await _cluster.DisposeAsync();
}
[Fact]
public async Task SaysHello()
{
var grain = _cluster.Client.GetGrain<IHelloGrain>(0);
var result = await grain.SayHello("World");
Assert.Equal("Hello, World!", result);
}
}
Konfigurieren des Testclusters
Verwenden Sie InProcessTestClusterBuilder, um Silos, Clients und Dienste zu konfigurieren:
var builder = new InProcessTestClusterBuilder(initialSilosCount: 2);
// Configure silos
builder.ConfigureSilo((options, siloBuilder) =>
{
siloBuilder.AddMemoryGrainStorage("Default");
siloBuilder.AddMemoryGrainStorage("PubSubStore");
});
// Configure clients
builder.ConfigureClient(clientBuilder =>
{
// Client-specific configuration
});
// Configure both silos and clients (shared services)
builder.ConfigureHost(hostBuilder =>
{
hostBuilder.Services.AddSingleton<IMyService, MyService>();
});
var cluster = builder.Build();
await cluster.DeployAsync();
InProcessTestClusterOptions
| Option | Typ | Standard | Description |
|---|---|---|---|
| ClusterId | string |
Automatisch generiert | Clusterbezeichner. |
| ServiceId | string |
Automatisch generiert | Dienstbezeichner. |
InitialSilosCount |
int |
1 | Die Anzahl der Silos, die zu Beginn starten sollen. |
InitializeClientOnDeploy |
bool |
true |
Gibt an, ob der Client bei der Bereitstellung automatisch initialisiert werden soll. |
ConfigureFileLogging |
bool |
true |
Aktivieren Sie die Dateiprotokollierung für das Debuggen. |
UseRealEnvironmentStatistics |
bool |
false |
Verwenden Sie reale Arbeitsspeicher-/CPU-Statistiken anstelle simulierter Werte. |
GatewayPerSilo |
bool |
true |
Gibt an, ob jedes Silo ein Gateway für Clientverbindungen hosten soll. |
Freigeben eines Testclusters zwischen Tests
Um die Testleistung zu verbessern, teilen Sie einen einzelnen Cluster über mehrere Testfälle mit xUnit-Geräten:
public class ClusterFixture : IAsyncLifetime
{
public InProcessTestCluster Cluster { get; private set; } = null!;
public async Task InitializeAsync()
{
var builder = new InProcessTestClusterBuilder();
builder.ConfigureSilo((options, siloBuilder) =>
{
siloBuilder.AddMemoryGrainStorageAsDefault();
});
Cluster = builder.Build();
await Cluster.DeployAsync();
}
public async Task DisposeAsync()
{
await Cluster.DisposeAsync();
}
}
[CollectionDefinition(nameof(ClusterCollection))]
public class ClusterCollection : ICollectionFixture<ClusterFixture>
{
}
[Collection(nameof(ClusterCollection))]
public class HelloGrainTests
{
private readonly ClusterFixture _fixture;
public HelloGrainTests(ClusterFixture fixture)
{
_fixture = fixture;
}
[Fact]
public async Task SaysHello()
{
var grain = _fixture.Cluster.Client.GetGrain<IHelloGrain>(0);
var result = await grain.SayHello("World");
Assert.Equal("Hello, World!", result);
}
}
Hinzufügen und Entfernen von Silos während Tests
Dies InProcessTestCluster unterstützt das dynamische Silomanagement für das Testen des Clusterverhaltens:
// Start with 2 silos
var builder = new InProcessTestClusterBuilder(initialSilosCount: 2);
var cluster = builder.Build();
await cluster.DeployAsync();
// Add a third silo
var newSilo = await cluster.StartSiloAsync();
// Stop a silo
await cluster.StopSiloAsync(newSilo);
// Restart all silos
await cluster.RestartAsync();
Verwenden Sie das TestCluster
Das TestCluster verwendet einen klassenbasierten Konfigurationsansatz, der die Implementierung der Schnittstellen ISiloConfigurator und IClientConfigurator erfordert. Dieses Design unterstützt Multiprozesstests, in denen Silos in separaten Prozessen ausgeführt werden, was für produktionsähnliche Simulationstests nützlich ist.
TestCluster wird jedoch standardmäßig auch im Prozess mit derselben Leistung wie InProcessTestCluster ausgeführt.
Wählen Sie TestCluster statt InProcessTestCluster wenn:
- Sie benötigen Multiprozesstests für die Produktionssimulation
- Sie verfügen über vorhandene Tests mit der
TestClusterAPI - Sie benötigen Kompatibilität mit Orleans 7.x oder 8.x.
Für neue Tests wird InProcessTestCluster aufgrund der einfacheren Delegate-basierten Konfiguration empfohlen.
Das Microsoft.Orleans.TestingHost NuGet-Paket enthält TestCluster, mit dem Sie einen Speichercluster (bestehend aus zwei Silos standardmäßig) zum Testen von Getreide erstellen können.
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);
}
}
Aufgrund des Mehraufwands beim Starten eines In-Memory-Clusters sollten Sie ein TestCluster erstellen und es unter mehreren Testfällen wiederverwenden. Erreichen Sie dies beispielsweise mithilfe von Klassen- oder Sammlungsfixaturen in xUnit.
Um einen TestCluster zwischen mehreren Testfällen zu teilen, erstellen Sie zuerst einen Vorrichtungstyp:
using Orleans.TestingHost;
public sealed class ClusterFixture : IDisposable
{
public TestCluster Cluster { get; } = new TestClusterBuilder().Build();
public ClusterFixture() => Cluster.Deploy();
void IDisposable.Dispose() => Cluster.StopAllSilos();
}
Erstellen Sie als Nächstes eine Sammlungseinrichtung:
[CollectionDefinition(Name)]
public sealed class ClusterCollection : ICollectionFixture<ClusterFixture>
{
public const string Name = nameof(ClusterCollection);
}
Sie können jetzt eine TestCluster in Ihren Testfällen wiederverwenden:
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);
}
}
Wenn alle Tests abgeschlossen sind und die In-Memory-Clustersilos beendet werden, ruft xUnit die Dispose() Methode des ClusterFixture Typs auf.
TestCluster hat außerdem einen Konstruktor, der TestClusterOptions akzeptiert, den Sie zum Konfigurieren der Silos im Cluster verwenden können.
Wenn Sie Dependency Injection in Ihrem Silo verwenden, um Dienste für Grains bereitzustellen, können Sie auch dieses Muster verwenden.
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)
{
// TODO: Call required service registrations here.
// siloBuilder.Services.AddSingleton<T, Impl>(/* ... */);
}
}
Verwenden Sie Mocks
Orleans ermöglicht auch das Simulieren vieler Teile des Systems. Für viele Szenarien ist dies der einfachste Weg, Unit-Tests für Grains durchzuführen. Dieser Ansatz hat Einschränkungen (beispielsweise bei der Terminierung von Reentrancy und Serialisierung) und erfordert möglicherweise Grains, die nur in Ihren Komponententests eingeschlossen werden. Das Orleans TestKit bietet einen alternativen Ansatz, bei dem viele dieser Einschränkungen querschritten werden.
Stellen Sie sich beispielsweise vor, dass das Getreide, das Sie testen, mit anderen Getreiden interagiert. Um diese anderen Körner zu verspotten, müssen Sie auch das GrainFactory-Mitglied des Korns, das getestet wird, nachahmen. Standardmäßig ist GrainFactory eine normale protected-Eigenschaft, aber die meisten Mocking-Frameworks erfordern, dass Eigenschaften public und virtual sind, um Mocking zu ermöglichen. Der erste Schritt besteht also darin, sowohl GrainFactory als auch public zu machen:
public new virtual IGrainFactory GrainFactory
{
get => base.GrainFactory;
}
Jetzt können Sie Ihre Komponente außerhalb der Orleans Laufzeit erstellen und Mocking verwenden, um das Verhalten von GrainFactory zu steuern.
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());
}
}
Erstellen Sie hier das Testkorn, WorkerGrain, mithilfe von Moq. Dies ermöglicht das Überschreiben des GrainFactoryVerhaltens, sodass es ein simuliertes IJournalGrainZurückgibt. Anschließend können Sie überprüfen, ob WorkerGrain wie erwartet mit IJournalGrain interagiert.