CQRS之旅——旅程4(扩展和增强订单和注册限界上下文) (11)

Jana(软件架构师)发言:

这些“UI之下”测试也被称为皮下测试(参见Meszaros, G。Melnik, G的Acceptance Test Engineering Guide)。

重写一遍已经在网站上实现的应用程序逻辑似乎有点多余,但是有以下几个原因值得花时间:

您(由于某些原因)对网站或任何其他基础设施部分的行为测试不感兴趣。你只对领域有兴趣,单元级和集成级的测试将验证代码的功能是否正确,因此不需要重复这些测试。

当与产品所有者迭代用户故事时,将时间花在纯粹的UI关注点上会拖慢反馈周期,降低反馈的质量和有用性。

考虑到不同的人在讨论技术问题时使用的词汇之间有时会出现很大的不匹配,用更抽象的术语讨论一个功能可以更好的理解业务试图解决的问题。

在实现测试逻辑时遇到的障碍可以帮助提高系统的总体设计质量。基础设施代码与应用程序逻辑难以分离通常被视为一种坏味道。

备注:为什么这些类型的测试是一个好主意?还有更多的原因没有列出来,但是对于本例来说,这里列出的是那些重要的原因。

Contoso会议管理系统的体系结构是松耦合的,利用消息将命令和事件传递给相关方。命令通过命令总线路由到单个处理程序,而事件则通过事件总线路由到它们的1个或多个处理程序。就消费应用程序而言,总线不绑定任何特定的技术,允许以对用户透明的方式在整个系统中创建和使用任意的实现。

当涉及到松耦合消息体系结构的行为测试时,另一个好处是BDD(或类似风格的)测试本身不涉及应用程序代码的内部工作。它们只关心被测试程序的可观察行为。这意味着对于SpecFlow测试,我们只需要将一些命令发布到总线,并通过根据实际的流量/数据断言预期的消息流量和有效负载来检查外部结果。

备注:在适当的地方,可以使用mock和stub来进行这些类型的测试。一个适当的例子是使用mock出来的ICommandBus对象而不是真正的AzureCommandBus类型。但mock一个完整的领域服务是不合适的例子。尽量少的使用mock,只把它限制在基础设施方面,这样你的生活和测试压力都会小很多。

另一种情况

我刚刚花费了很多来描述事情是多么的棒和简单,哪里有痛苦呢?痛苦在于理解一个系统中发生了什么。松耦合的体系结构也有不好的一面:控制反转和依赖注入等技术从本质上阻碍了代码的可读性,因为如果不仔细检查容器的初始化,就永远无法确定在特定的点注入了什么具体的类。在journey的代码中,IProcess接口是一种表示长时间运行的业务流程(也称为Sagas或流程管理器)的类,这些类负责协调不同聚合之间的业务逻辑。为了维护系统数据和状态的完整性、幂等性和事务性,它发出的命令的实际发送是各个持久化仓储来实现的。由于控制反转和依赖注入对消费者隐藏了这些类型的详细信息,所以它和系统的一些其他属性会造成一点困难在回答一些表面上琐碎的问题时,比如:

谁会发出或已发出了特定的命令或事件?

什么样的类处理特定的命令或事件?

流程或聚合在哪里创建或持久化?

什么时候发出与其他命令或事件相关的命令?

为什么系统会这样运行?

应用程序的状态如何由特定的命令改变?

由于应用程序的依赖关系非常松散,许多传统的代码分析工具和方法要么变得不那么有用,要么完全没用。

让我们以RegistrationProcessManager作为示例,列出一些涉及到回答这些问题的启发式内容。

打开RegistrationProcessManager.cs文件,注意,与许多流程管理器一样,它有一个ProcessState枚举。我们注意进程的开始状态:NotStarted。接下来,我们要找到做下面事情之一的代码:

创建流程的新实例(流程在哪里创建或持久化?)

初始状态被更改为不同的状态(状态如何更改?)

找到源代码中出现上述任何一种情况或同时出现上述两种情况的代码位置。在本例中,它是RegistrationProcessManagerRouter类中的Handle方法。重要提示:这并不一定意味着该流程是一个命令处理程序!流程管理器负责从存储中创建和检索聚合根(AR),以便将消息路由到AR,因此尽管它们的方法在名称和签名上与ICommandHandler实现类似,但它们并不实现处理命令的逻辑。

请注意当状态发生变化时接收到的消息类型是作为方法参数被传入的,因此我们现在需要找出消息的来源。

我们还将注意到,RegistrationProcessManager发出了一个新的命令:MakeSeatReservation

如上所述,这个命令实际上不是由发出它的进程发出的,相反,是当进程保存到磁盘时,才会发出。

对于其他任何作为进程处理命令的副作用的,被发出的命令,需要一定程度的重复这些启发。

查找OrderPlaced的引用,找到一个或多个顶部(外部)组件,这些组件通过ICommandBus接口上的Send方法发出该类型的消息。

由于内部发出的命令是在仓储的Save方法里,所以可以安全地假设直接调用Send方法的任何非基础设施逻辑都是外部入口点。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wppzzg.html