【问题标题】:How to test Automatonymous state machines?如何测试自动状态机?
【发布时间】:2018-06-22 15:12:01
【问题描述】:

我正在尝试为 Automatonymous 状态机编写测试,但我在正确处理它时遇到了一些麻烦,而且我发现的文档很少。

这是我目前的一项测试:

[TestFixture]
public class MyProcessStateMachineTests
{
    InMemoryTestHarness _Harness;
    MyProcessStateMachine _Machine;
    StateMachineSagaTestHarness<MyProcess, MyProcessStateMachine> _Saga;

    [OneTimeSetUp]
    public void ConfigureMessages()
    {
        MessageCorrelation.UseCorrelationId<RequestMyDetails>(x => x.CorrelationId);
        MessageCorrelation.UseCorrelationId<FileAttached>(x => x.CorrelationId);
        MessageCorrelation.UseCorrelationId<PDFGenerated>(x => x.CorrelationId);
        MessageCorrelation.UseCorrelationId<CustomerAttachFile>(x => x.CorrelationId);
        MessageCorrelation.UseCorrelationId<AddCustomerNote>(x => x.CorrelationId);
        MessageCorrelation.UseCorrelationId<EmailPublished>(x => x.CorrelationId);
    }


    [SetUp]
    public void InitializeTestHarness()
    {
        _Harness = new InMemoryTestHarness();
        _Machine = new MyProcessStateMachine( /* snip */ );
        _Saga = _Harness.StateMachineSaga<MyProcess, MyProcessStateMachine>(_Machine);

        _Harness.Start().Wait();
    }

    [TearDown]
    public void StopTestHarness()
    {
        _Harness.Stop();
    }


    [Test]
    public async Task ShouldAttachToCustomer()
    {
        var sagaId = Guid.NewGuid();
        var custId = Guid.NewGuid();
        var fileAttached = BuildFileAttachedMessage(sagaId);

        await _Harness.InputQueueSendEndpoint.Send(BuildStartMessage(sagaId));
        await _Harness.InputQueueSendEndpoint.Send(BuildDetailsReceivedMessage(sagaId));
        await _Harness.InputQueueSendEndpoint.Send(BuildPdfGeneratedMessage(sagaId));
        await _Harness.InputQueueSendEndpoint.Send(fileAttached);

        // Next line is based on [the answer here][1]
        // Once the above messages are all consumed and processed,
        // the state machine should be in AwaitingEmail state
        await _Saga.Match(x =>
            x.CorrelationId == sagaId
                && x.CurrentState == _Machine.AwaitingEmail.Name,
            new TimeSpan(0, 0, 30));

        // Grab the instance and Assert stuff...
    }

    // Snip...
}

鉴于 _Saga.Match 调用找到了匹配项,我希望所有消息都已处理,我应该能够获取我的状态机实例和已发布的事件并检查它们的值 - 但事实并非如此。当我在夹具中运行测试时,有时我得到的实例已经消耗并发布了预期的消息;有时它还没有完全到位。

我尝试使用以下方法获取我的实例:

var inst = _Saga.Sagas.FirstOrDefault(x => x.Saga.CorrelationId == sagaId);

或通过以下方式获取已发布的事件:

var test = _Harness.Published
    .FirstOrDefault(x => x.MessageType == typeof(IAttachFile) && x.Context.CorrelationId == sagaId);

但是对 Match 的调用是否成功并不重要,状态机实例(和已发布的事件)并不总是存在。

我假设来自 Automatonymous、MassTransit 或测试工具的异步进程导致了不一致。有什么帮助吗?

使用 MassTransit、MassTransit.Automatonymous 和 MassTransit.TestFramework 5.1.2.1528、Automatonymous 4.1.1.102 进行测试,

编辑:

进一步审查,我发现当我遇到问题时,拨打Match( ... ) 没有成功 - 它超时。 (我一直错误地认为超时会引发异常。)

【问题讨论】:

  • 检查这些测试,它们实际上展示了如何使用测试工具来测试状态机传奇github.com/MassTransit/MassTransit/blob/develop/src/…
  • 是的,我之前检查过这些测试。再试一次,以防我第一次错过了什么。使用基于 _Harness.Consumed 的检查,从 _Saga.Created 获取实例形式(如示例测试 Using_the_testing_framework_built_into_masstransit)。在调用和不调用 _Saga.Match 的情况下运行测试,在某些测试中使用断点进行调试,而不是在其他测试中。针对实例的当前 CurrentState 断言有时会通过,通常会失败。似乎在我只使用一两条消息的测试中更频繁地工作。如果我尝试进一步进入状态机,事情不会。
  • 我会避免使用MassTransit.TestFramework,因为它与 NUnit 耦合。我们正在使用MassTransit.Testing 测试所有内容,没有任何问题。
  • @AlexeyZimarev MassTransit.Testing 还在吗?
  • 对于遇到相同问题的任何人:您尝试获取实例的方式 var inst = _Saga.Sagas.FirstOrDefault(x => x.Saga.CorrelationId == sagaId);

标签: c# testing masstransit automatonymous


【解决方案1】:

对于遇到相同问题的任何人:您尝试获取实例的方式

var inst = _Saga.Sagas.FirstOrDefault(x =&gt; x.Saga.CorrelationId == sagaId);

可以这样修复

var instanceIds = await _sagaHarness.Match(
    instance => instance.CorrelationId == sagaId
        && instance.CurrentState == _machine.Working.Name,
        new TimeSpan(0, 0, 30));

条件中包含预期状态很重要。仅通过correlationid 获取实例然后测试CurrentState 可能会失败,因为设置状态(saga 执行是异步的)可能需要一些时间。 完整示例见https://github.com/balintn22/AutomatonymousTestExample

【讨论】:

    【解决方案2】:

    如果这可能对其他人有帮助,这就是我最终得到它的方式:

    [TestFixture]
    public class ProcessStateMachineTests : InMemoryTestFixture
    {
        TimeSpan _TestTimeout = new TimeSpan(0, 1, 0);
        ProcessStateMachine _Machine;
        InMemorySagaRepository<Process> _Repository;
    
    
        protected override void ConfigureInMemoryReceiveEndpoint(
            IInMemoryReceiveEndpointConfigurator configurator)
        {
            _Machine = new ProcessStateMachine();
            _Repository = new InMemorySagaRepository<Process>();
    
            configurator.StateMachineSaga(_Machine, _Repository);
        }
    
    
        [OneTimeSetUp]
        public void ConfigureMessages()
        {
            // Message correlation and such happens in here
            ProcessStateMachine.ConfigureMessages();
        }
    
        [Test]
        public async Task OnInitializationIStartProcessIsConsumed()
        {
            var sagaId = Guid.NewGuid();
            var customerId = Guid.NewGuid();
    
            await SetupStateMachine(sagaId, customerId, _Machine.AwaitingDetails.Name);
    
            var msg = InMemoryTestHarness.Consumed
                .Select<IStartProcess>(x => x.Context.Message.RequestId == sagaId)
                .FirstOrDefault();
    
            // Assert against msg for expected results
        }
    
        [Test]
        public async Task OnStartProcessAddCustomerNoteAndRequestDetailsPublished()
        {
            var sagaId = Guid.NewGuid();
            var customerId = Guid.NewGuid();
    
            await SetupStateMachine(sagaId, customerId, _Machine.AwaitingDetails.Name);
    
            var pubdNoteAddedMsg = InMemoryTestHarness.Published
                .Select<IAddCustomerNote>()
                .FirstOrDefault(x => x.Context.Message.RequestId == sagaId);
            var pubdDetailsReqdMsg = InMemoryTestHarness.Published
                .Select<IRequestDetails>()
                .FirstOrDefault(x => x.Context.Message.RequestId == sagaId);
    
            Assert.IsTrue(pubdNoteAddedMsg != null);
            Assert.IsTrue(pubdDetailsReqdMsg != null);
    
            Assert.AreEqual(sagaId, pubdNoteAddedMsg.Context.CorrelationId);
            Assert.AreEqual(sagaId, pubdDetailsReqdMsg.Context.CorrelationId);
    
            Assert.AreEqual(customerId, pubdNoteAddedMsg.Context.Message.CustomerId);
            Assert.IsFalse(String.IsNullOrEmpty(pubdNoteAddedMsg.Context.Message.Note));
        }
    
    
        private async Task SetupStateMachine(
            Guid sagaId,
            Guid customerId,
            String toState)
        {
            if (String.IsNullOrEmpty(toState))
                return;
    
            await MoveStateMachineForward(BuildStartMessage(), x => x.AwaitingDetails);
    
            var awaitingDetailsId = await _Repository.ShouldContainSagaInState(
                sagaId, _Machine, x => x.AwaitingDetails, _TestTimeout);
    
            Assert.IsNotNull(awaitingDetailsId, "Error, expected state machine in AwaitingDetails state");
    
            if (toState == _Machine.AwaitingDetails.Name)
                return;
    
            // ...and more stuff to move to later states, depending on
            // where I want my test's starting point to be...
    
    
            async Task MoveStateMachineForward<T>(
                T message,
                Func<ProcessStateMachine, Automatonymous.State> targetState)
                where T : class
            {
                await InputQueueSendEndpoint.Send(message);
    
                var foundSagaId = await _Repository.ShouldContainSagaInState(
                    sagaId, _Machine, targetState, _TestTimeout);
    
                Assert.IsTrue(foundSagaId.HasValue);
            }
    
    
            IStartProcess BuildStartMessage()
            {
                return new StartProcessMessage(sagaId, customerId);
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-06-24
      • 2016-06-09
      • 1970-01-01
      • 1970-01-01
      • 2012-01-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多