Модульное тестирование с помощью Orleans
В этом руководстве показано, как модульное тестирование зерна, чтобы убедиться, что они работают правильно. Существует два основных способа модульного тестирования зерна, и выбранный метод будет зависеть от типа функциональности, которую вы тестируете. Microsoft .Orleans. Пакет NuGet TestHost можно использовать для создания тестовых силосов для зерна или использовать макетную платформу, например 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 вызывает Dispose() метод ClusterFixture
типа, когда все тесты были завершены, а оси кластера в памяти остановлены. TestCluster
также имеет конструктор, который принимает TestClusterOptions , что может использоваться для настройки силосов в кластере.
Если вы используете внедрение зависимостей в Silo, чтобы сделать службы доступными для grains, вы также можете использовать этот шаблон:
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());
}
}
Здесь вы создаете тестируемое зерно, WorkerGrain
используя Moq, что означает, что вы можете переопределить поведение GrainFactory
так, чтобы он возвращал IJournalGrain
макет. Затем можно убедиться, что WorkerGrain
взаимодействие с IJournalGrain
ожидаемым.