使用 Orleans 的單元測試
本教學課程說明如何對粒紋進行單元測試,以確保運作正常。 有兩種主要方式可對粒紋進行單元測試,您選擇的方法將取決於您正在測試的功能型別。 Microsoft.Orleans.TestingHost NuGet 套件可用來為粒紋建立測試定址接收器,或者您可以使用 Moq 之類的模擬架構,來模擬粒紋與之互動的 Orleans 執行階段部分。
使用 TestCluster
Microsoft.Orleans.TestingHost
NuGet 封裝包含的 TestCluster,可用來建立記憶體內部叢集,其預設組成為可用來測試粒紋的兩個定址接收器。
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);
}
}
由於啟動記憶體內部叢集的額外負荷,建議您建立 TestCluster
,並在多個測試案例之間重複使用。 例如,您可以使用 xUnit 的類別或集合固件來完成此作業。
若要在多個測試案例之間共用 TestCluster
,請先建立固件型別:
using Orleans.TestingHost;
public sealed class ClusterFixture : IDisposable
{
public TestCluster Cluster { get; } = new TestClusterBuilder().Build();
public ClusterFixture() => Cluster.Deploy();
void IDisposable.Dispose() => Cluster.StopAllSilos();
}
接下來,建立集合固件:
[CollectionDefinition(Name)]
public sealed class ClusterCollection : ICollectionFixture<ClusterFixture>
{
public const string Name = nameof(ClusterCollection);
}
您現在可以在測試案例中重複使用 TestCluster
:
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 會呼叫 ClusterFixture
型別的 Dispose() 方法。 TestCluster
也有可接受 TestClusterOptions 的建構函式,可用來設定叢集中的定址接收器。
如果您在定址接收器中使用相依性插入讓服務可供粒紋使用,則也可以使用此模式:
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>(/* ... */);
});
}
}
使用模擬
Orleans 也可讓您模擬系統的許多部分,且在許多情節中,這是對粒紋進行單元測試的最簡單方式。 此方法有限制 (例如排程重新進入和序列化),且可能需要粒紋只包含單元測試所使用的程式碼。 Orleans TestKit 提供替代方法,可避免許多限制。
例如,想像要測試的粒紋與其他粒紋互動。 為了能夠模擬那些其他粒紋,我們也需要模擬受測粒紋的 GrainFactory 成員。 根據預設 GrainFactory
是一般 protected
屬性,但大部分的模擬架構都要求屬性為 public
與 virtual
,以便能夠進行模擬。 因此,我們需要做的第一件事,是將 GrainFactory
設為 public
和 virtual
屬性:
public new virtual IGrainFactory GrainFactory
{
get => base.GrainFactory;
}
現在,我們可以在 Orleans 執行階段之外建立粒紋,並使用模擬來控制 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());
}
}
在這裡,您會使用 Moq 建立受測粒紋 WorkerGrain
,這表示您可以接著覆寫 GrainFactory
的行為,讓其傳回模擬的 IJournalGrain
。 然後,您可以驗證 WorkerGrain
如預期與 IJournalGrain
互動。