Unit testing 单元测试域对象

Unit testing 单元测试域对象,unit-testing,domain-driven-design,moq,aggregateroot,Unit Testing,Domain Driven Design,Moq,Aggregateroot,我们要求在用户在活动页面上输入其电子邮件地址时添加活动提醒。事件是另一个域对象。我们最初的想法是创建一个客户域对象和相关的CustomerService: public class CustomerService { public void AddEventReminder(string emailAddress, int eventId) { var customer = new Customer(emailAddress); customer.AddEma

我们要求在用户在活动页面上输入其电子邮件地址时添加活动提醒。事件是另一个域对象。我们最初的想法是创建一个客户域对象和相关的CustomerService:

public class CustomerService {
    public void AddEventReminder(string emailAddress, int eventId) {
       var customer = new Customer(emailAddress);
       customer.AddEmailReminder(eventId);
    }
}
我们如何在单元测试中验证AddEmailReminder方法是否确实在新客户上被调用

我的想法:

  • 使用工厂创建客户。这很难闻,因为我以为你只应该在对象创建过程中存在一些复杂性的地方使用factory
  • 错误代码。也许有更好的办法
  • 魔术师

  • 另一方面(可能是相关的),我们如何确定这里的聚合根?我们已经武断地决定了客户是谁,但它同样可能是事件。我已经阅读并理解了有关聚合根的文章,但在这种情况下并不清楚。

    在这种情况下,我会在创建客户的服务中创建一个受保护的方法,在测试中使用匿名内部类覆盖该方法,并使其返回一个模拟客户对象。然后,您可以在模拟客户对象上验证是否调用了AddEmailReminder。 比如:

    public class CustomerService {
        public void AddEventReminder(string emailAddress, int eventId) {
           var customer = createCustomer(emailAddress);
           customer.AddEmailReminder(eventId);
        }
    
        protected Customer createCustomer(string emailAddress) {
           return new Customer(emailAddress);
        }
    }
    
    在测试中(假设C#知识有限,但它应该说明这一点):


    对CustomerService API的思考

    您决定将此操作封装到CustomerService中有什么特殊原因吗?我觉得这有点像。它是否可以直接封装在客户身上

    也许您遗漏了CustomerService代码示例中的一些内容来简化事情

    但是,如果必须更改签名以获取客户实例,则可以解决以下问题:

    public void AddEventReminder(Customer customer, int eventId)
    
    但话说回来,Int32很难限定为域对象,因此签名应该是

    public void AddEventReminder(Customer customer, Event event)
    
    现在的问题是,这种方法是否会增加任何价值

    哪个是聚合根?

    我想他们都没有。聚合根表示仅通过根管理子级,在这种情况下,这两种方式都没有意义

    考虑以下选项:

    如果将Event设为root,则意味着您可能没有CustomerRepository,而检索、编辑和持久化客户的唯一方法是通过事件。我觉得这听起来很不对

    如果将Customer作为根用户,那么就不可能有EventRepository,只有通过特定的客户才能检索、编辑和持久化事件。对我来说,这听起来同样错误


    唯一剩下的可能性是它们是独立的根。这也意味着它们只是松散地相互连接,您需要某种域服务来查找客户的事件,或事件的客户。

    此域服务方法的全部要点是,它直接从应用程序服务调用,该应用程序服务已收到带有电子邮件地址和事件id的dto。我认为域对象应仅处理完整对象,因此域服务应处理此问题。我不明白为什么它是贫乏的意味着它不应该在域服务上运行,如果我认为它是相反的话,因为域对象都是关于逻辑的。关于聚合根,我同意它们都是聚合根,我问的问题是哪个聚合根负责关联?…有趣的答案。如果我要更改签名,什么代码将负责创建客户聚合根?也许这是更相关的问题。我怀疑该服务是从应用层映射的。然而,在这种情况下,它不是一个域服务——它是一个应用层服务,属于该层。在这种情况下,我认为您需要一个抽象工厂(可能是CustomerRepository),它可以根据电子邮件地址查找现有客户。关于关联事件和客户的责任问题:当它们都不是彼此的聚合根时,它们都不能承担该责任。新的域服务必须承担这一责任。
    public void AddEventReminder(Customer customer, Event event)