使用 NserviceBus.Testing 当代码不是通过消息处理程序启动时,你如何测试发布的事件
Using NserviceBus.Testing how do you test the published event when the code is not initiated via a message handler
问题
如果当前正在测试的方法是通过传入消息(进入处理程序)以外的其他方法启动的,您如何测试事件的发布。 NServiceBus.Testing 的问题(据我所知)是它非常适合测试处理程序,这反过来导致 messages/events 成为 send/published。
背景:
我们有许多遗留系统,在不断努力转向适当的 SOA 实施的过程中,我们需要与一些遗留数据库作业集成。这些作业每 15 分钟对数据库执行一次操作,我们希望在某些情况发生时挂钩并发布事件。
我们有一个 windows 服务,它在 NsbHost 中 运行 但不包含任何处理程序。相反,我们使用 Quartz 创建一个每分钟运行一次的内部 cron 作业,轮询数据库以查找与指定模式匹配的记录,并在更新数据库之前发布一个事件,表明我们已经处理了该记录。这就是我们整合新旧系统的方式。
显然这不是一个理想的情况,但考虑到我们正处于 SOA 实施的过渡阶段,它和我们现在一样好。
详情及代码:
我们发布的事件界面是这样的
public interface IPremiumAdjustmentFinalised
{
string PolicyNumber { get; set; }
decimal Amount { get; set; }
DateTime FinalisedOn { get; set; }
}
实际发布活动的代码是
Bus.Publish<IPremiumAdjustmentFinalised>(e =>
{
e.Amount = AdjustmentAmount;
e.PolicyNumber = PolicyNumber;
e.FinalisedOn = LastModifiedOn;
});
一切正常,我们可以测试调用是使用最小起订量进行的:
eventPublisher.MethodToTest();
bus.Verify(x => x.Publish(It.IsAny<Action<IPremiumAdjustmentFinalised>>()), Times.Once);
其中 bus 是插入到构造函数中的新 Mock。
但我想测试 IPremiumAdjustmentFinalised 中的值。我发现这真的很难我认为主要是因为它是一个接口而不是具体的 class.
我们已尝试使用 NServiceBus.Testing 来尝试检查生成的事件,但无济于事。
有谁知道如何在此给定场景中测试事件中的值?
我们提出了 2 个解决方案
解决方案一:
创建一个具体的 class 来继承接口和总线发布
public class PremiumAdjustmentFinalisedEvent : IPremiumAdjustmentFinalised
{
public string PolicyNumber { get; set; }
public decimal Amount { get; set; }
public DateTime FinalisedOn { get; set; }
}
这个class只在发送方使用,另一端的处理程序仍然会监听 IPremiumAdjustmentFinalised 类型的事件。
发送事件会变成这样
var message = new PremiumAdjustmentFinalisedEvent
{
Amount = AdjustmentAmount,
PolicyNumber = PolicyNumber,
FinalisedOn = LastModifiedOn
};
Bus.Publish<IPremiumAdjustmentFinalised>(message);
这让我们可以这样测试:
bus.Verify(x => x.Publish(It.Is<IPremiumAdjustmentFinalised>(paf =>
paf.Amount == AdjustmentAmount &&
paf.FinalisedOn == LastModifiedOn &&
paf.PolicyNumber == PolicyNumber)), Times.Once);
这不是一个理想的解决方案,因为我们需要向实时解决方案添加代码以启用测试,但它运行良好且易于理解。
方案二:
实现我们自己的 IBus 并断言对象在其中是正确的:
class myBus : IBus
{
public void Publish<T>(Action<T> messageConstructor)
{
IPremiumAdjustmentFinalised paf = new PremiumAdjustmentFinalised();
var ipaf = (T) paf;
messageConstructor(ipaf);
Assert.AreEqual(paf.Amount, AdjustmentAmount);
Assert.AreEqual(paf.FinalisedOn, LastModifiedOn);
Assert.AreEqual(paf.PolicyNumber, PolicyNumber);
}
...
<<Leave the rest of the methods not implemented>>
...
}
使用我自己的 IPremiumAdjustmentFinalised 测试实现(类似于解决方案 1,但在测试项目中)
现在测试看起来像这样
var myBus = new myBus();
eventPublisher = new AdjustmentFinaliserEventPublisher(myBus);
eventPublisher.UpdateAndPublishFinalisedAdjustments();
我喜欢这个解决方案,因为它不会仅仅为了测试而更改实时代码,但缺点是有一个很大的 IBus 实现,其中包含大量未实现的方法。
在找到这篇文章后搜索 google 结果几天后,除了这篇文章之外似乎仍然没有真正的指导。实施解决方案 2 后,我决心找到一种更轻松的方法,并使用 Moq 作为模拟库提出了以下方案。
///Declarations
private Mock<IPremiumAdjustmentFinalised > _mockEvent = new Mock<IPremiumAdjustmentFinalised >();
private readonly Mock<IBus> _mockBus = new Mock<IBus>();
private SomeEventPublisher _someEventPublisher;
[SetUp]
public void Setup()
{
//Instruct moq to set up implementations for the event interface's properties.
_mockEvent.SetupAllProperties();
_mockBus.Setup(b => b.Publish(It.IsAny<Action<IPremiumAdjustmentFinalised>>()))
.Callback((Action<IPremiumAdjustmentFinalised> messageConstructor) =>
{
//Execute the constructor provided in the action in the event publisher, so the event data can be interrogated.
messageConstructor(_mockEvent.Object);
});
_someEventPublisher = new SomeEventPublisher(_mockBus);
}
[Test]
public void SomeEventPublisherTest()
{
_someEventPublisher.PublishSomeEvent(AdjustmentAmount, LastModifiedOn, PolicyNumber)
Assert.AreEqual(_mockEvent.Object.Amount, AdjustmentAmount);
Assert.AreEqual(_mockEvent.Object.FinalisedOn, LastModifiedOn);
Assert.AreEqual(_mockEvent.Object.PolicyNumber, PolicyNumber);
}
问题
如果当前正在测试的方法是通过传入消息(进入处理程序)以外的其他方法启动的,您如何测试事件的发布。 NServiceBus.Testing 的问题(据我所知)是它非常适合测试处理程序,这反过来导致 messages/events 成为 send/published。
背景:
我们有许多遗留系统,在不断努力转向适当的 SOA 实施的过程中,我们需要与一些遗留数据库作业集成。这些作业每 15 分钟对数据库执行一次操作,我们希望在某些情况发生时挂钩并发布事件。
我们有一个 windows 服务,它在 NsbHost 中 运行 但不包含任何处理程序。相反,我们使用 Quartz 创建一个每分钟运行一次的内部 cron 作业,轮询数据库以查找与指定模式匹配的记录,并在更新数据库之前发布一个事件,表明我们已经处理了该记录。这就是我们整合新旧系统的方式。
显然这不是一个理想的情况,但考虑到我们正处于 SOA 实施的过渡阶段,它和我们现在一样好。
详情及代码:
我们发布的事件界面是这样的
public interface IPremiumAdjustmentFinalised
{
string PolicyNumber { get; set; }
decimal Amount { get; set; }
DateTime FinalisedOn { get; set; }
}
实际发布活动的代码是
Bus.Publish<IPremiumAdjustmentFinalised>(e =>
{
e.Amount = AdjustmentAmount;
e.PolicyNumber = PolicyNumber;
e.FinalisedOn = LastModifiedOn;
});
一切正常,我们可以测试调用是使用最小起订量进行的:
eventPublisher.MethodToTest();
bus.Verify(x => x.Publish(It.IsAny<Action<IPremiumAdjustmentFinalised>>()), Times.Once);
其中 bus 是插入到构造函数中的新 Mock。
但我想测试 IPremiumAdjustmentFinalised 中的值。我发现这真的很难我认为主要是因为它是一个接口而不是具体的 class.
我们已尝试使用 NServiceBus.Testing 来尝试检查生成的事件,但无济于事。
有谁知道如何在此给定场景中测试事件中的值?
我们提出了 2 个解决方案
解决方案一:
创建一个具体的 class 来继承接口和总线发布
public class PremiumAdjustmentFinalisedEvent : IPremiumAdjustmentFinalised
{
public string PolicyNumber { get; set; }
public decimal Amount { get; set; }
public DateTime FinalisedOn { get; set; }
}
这个class只在发送方使用,另一端的处理程序仍然会监听 IPremiumAdjustmentFinalised 类型的事件。
发送事件会变成这样
var message = new PremiumAdjustmentFinalisedEvent
{
Amount = AdjustmentAmount,
PolicyNumber = PolicyNumber,
FinalisedOn = LastModifiedOn
};
Bus.Publish<IPremiumAdjustmentFinalised>(message);
这让我们可以这样测试:
bus.Verify(x => x.Publish(It.Is<IPremiumAdjustmentFinalised>(paf =>
paf.Amount == AdjustmentAmount &&
paf.FinalisedOn == LastModifiedOn &&
paf.PolicyNumber == PolicyNumber)), Times.Once);
这不是一个理想的解决方案,因为我们需要向实时解决方案添加代码以启用测试,但它运行良好且易于理解。
方案二:
实现我们自己的 IBus 并断言对象在其中是正确的:
class myBus : IBus
{
public void Publish<T>(Action<T> messageConstructor)
{
IPremiumAdjustmentFinalised paf = new PremiumAdjustmentFinalised();
var ipaf = (T) paf;
messageConstructor(ipaf);
Assert.AreEqual(paf.Amount, AdjustmentAmount);
Assert.AreEqual(paf.FinalisedOn, LastModifiedOn);
Assert.AreEqual(paf.PolicyNumber, PolicyNumber);
}
...
<<Leave the rest of the methods not implemented>>
...
}
使用我自己的 IPremiumAdjustmentFinalised 测试实现(类似于解决方案 1,但在测试项目中)
现在测试看起来像这样
var myBus = new myBus();
eventPublisher = new AdjustmentFinaliserEventPublisher(myBus);
eventPublisher.UpdateAndPublishFinalisedAdjustments();
我喜欢这个解决方案,因为它不会仅仅为了测试而更改实时代码,但缺点是有一个很大的 IBus 实现,其中包含大量未实现的方法。
在找到这篇文章后搜索 google 结果几天后,除了这篇文章之外似乎仍然没有真正的指导。实施解决方案 2 后,我决心找到一种更轻松的方法,并使用 Moq 作为模拟库提出了以下方案。
///Declarations
private Mock<IPremiumAdjustmentFinalised > _mockEvent = new Mock<IPremiumAdjustmentFinalised >();
private readonly Mock<IBus> _mockBus = new Mock<IBus>();
private SomeEventPublisher _someEventPublisher;
[SetUp]
public void Setup()
{
//Instruct moq to set up implementations for the event interface's properties.
_mockEvent.SetupAllProperties();
_mockBus.Setup(b => b.Publish(It.IsAny<Action<IPremiumAdjustmentFinalised>>()))
.Callback((Action<IPremiumAdjustmentFinalised> messageConstructor) =>
{
//Execute the constructor provided in the action in the event publisher, so the event data can be interrogated.
messageConstructor(_mockEvent.Object);
});
_someEventPublisher = new SomeEventPublisher(_mockBus);
}
[Test]
public void SomeEventPublisherTest()
{
_someEventPublisher.PublishSomeEvent(AdjustmentAmount, LastModifiedOn, PolicyNumber)
Assert.AreEqual(_mockEvent.Object.Amount, AdjustmentAmount);
Assert.AreEqual(_mockEvent.Object.FinalisedOn, LastModifiedOn);
Assert.AreEqual(_mockEvent.Object.PolicyNumber, PolicyNumber);
}