Orleans 2.0 官方文档 —— 8.8 实现细节 -> 单元测试

单元测试

本教程介绍如何对grain进行单元测试,以确保它们的行为正确。有两种主要方法可以对grain进行单元测试,您选择的方法取决于您正在测试的功能类型。该Microsoft.Orleans.TestingHostNuGet包可以用来为您的grain创建测试silo,也可以使用模拟框架(例如Moq),来模拟与你的grain交互的Orleans运行时部分。

使用TestCluster

Microsoft.Orleans.TestingHostNuGet包包含TestCluster,可用于创建一个内存中的集群,默认情况下由两个silo组成,可用于测试grain。

using System;
using System.Threading.Tasks;
using Orleans;
using Orleans.TestingHost;
using Xunit;

namespace Tests
{
    public class HelloGrainTests
    {
        [Fact]
        public async Task SaysHelloCorrectly()
        {
            var cluster = new TestCluster();
            cluster.Deploy();

            var hello = cluster.GrainFactory.GetGrain<IHelloGrain>(Guid.NewGuid());
            var greeting = await hello.SayHello();

            cluster.StopAllSilos();

            Assert.Equal("Hello, World", greeting);
        }
    }
}

由于启动内存中集群的开销,您可能希望创建一个TestCluster,并在多个测试用例中重用它。例如,这可以使用xUnit的类或fixtures集合来完成(有关更多详细信息,请参阅https://xunit.github.io/docs/shared-context.html)。

为了在多个测试用例之间共享一个TestCluster,首先创建一个fixture类型:

public class ClusterFixture : IDisposable
{
    public ClusterFixture()
    {
        this.Cluster = new TestCluster();
        this.Cluster.Deploy();
    }

    public void Dispose()
    {
        this.Cluster.StopAllSilos();
    }

    public TestCluster Cluster { get; private set; }
}

接下来创建一个fixture集合:

[CollectionDefinition(ClusterCollection.Name)]
public class ClusterCollection : ICollectionFixture<ClusterFixture>
{
    public const string Name = "ClusterCollection";
}

您现在可以在测试用例中重用一个TestCluster

using System;
using System.Threading.Tasks;
using Orleans;
using Xunit;

namespace Tests
{
    [Collection(ClusterCollection.Name)]
    public class HelloGrainTests
    {
        private readonly TestCluster _cluster;

        public HelloGrainTests(ClusterFixture fixture)
        {
            _cluster = fixture.Cluster;
        }

        [Fact]
        public async Task SaysHelloCorrectly()
        {
            var hello = _cluster.GrainFactory.GetGrain<IHelloGrain>(Guid.NewGuid());
            var greeting = await hello.SayHell();

            Assert.Equal("Hello, World", greeting);
        }
    }
}

当所有测试都已完成,并且内存中的集群的silo将被停止时,xUnit将调用ClusterFixture类型的Dispose方法。TestCluster还有一个接受TestClusterOptions的构造函数,可用于配置集群中的silo。

如果您在Silo中使用依赖注入为Grains提供服务,您也可以使用此模式:

public class ClusterFixture : IDisposable
{
    public ClusterFixture()
    {
        var builder = new TestClusterBuilder();
        builder.AddSiloBuilderConfigurator<TestSiloConfigurations>();
        this.Cluster = builder.Build();
        this.Cluster.Deploy();
    }

    public void Dispose()
    {
        this.Cluster.StopAllSilos();
    }

    public TestCluster Cluster { get; private set; }
}

public class TestSiloConfigurations : ISiloBuilderConfigurator {
    public void Configure(ISiloHostBuilder hostBuilder) {
        hostBuilder.ConfigureServices(services => {
            services.AddSingleton<T, Impl>(...);
        });
    }
}

使用Mocks(模拟)

Orleans还可以模拟系统的许多部分,对于许多场景,这是单元测试grain的最简单方法。这种方法确实存在局限性(例如,在调度可重入性和序列化方面),并且可能要求grain包括仅由单元测试使用的代码。Orleans TestKit提供了一种替代方法,它克服了这些限制。

例如,让我们想象一下,我们正在测试的grain与其他grain相互作用。为了能够模拟那些其他grain,我们还需要模拟被测grain的GrainFactory成员。默认情况下,GrainFactory是一个普通的protected属性,但大多数模拟框架都要求属性是publicvirtual,以便能够模拟它们。因此,我们要做的第一件事,就是让GrainFactory同时成为publicvirtual

public new virtual IGrainFactory GrainFactory
{
    get { return base.GrainFactory; }
}

现在我们可以在Orleans运行时之外,创建我们的grain,并使用mocking来控制GrainFactory的行为:

using System;
using System.Threading.Tasks;
using Orleans;
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,创建我们的测试grain WorkerGrain,这意味着我们可以覆盖GrainFactory的行为,以便它返回一个模拟的IJournalGrain。然后,我们可以验证我们的WorkerGrainIJournalGrain的交互,是否如我们期望的那样。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值