对包含从数据库填充的列表的域模型进行单元测试

Unit Testing a Domain Model Containing Lists Populated From The Database

我目前正在为我的领域模型编写单元测试。

为了说明一些上下文,我有一个角色组 class,它有一个角色列表,它还有一个当前分配给他们的角色组的用户列表。

一个角色组被分配给一个用户,所以所有的方法都在用户域模型中。这意味着角色组端的用户 属性 基本上是从数据库中提取的。

角色组和用户都是聚合根,它们都可以独立存在。

对包含从数据库填充的列表的域模型进行单元测试

我苦苦挣扎的是我无法测试下面的 CanArchive 方法,因为我无法将用户添加到 属性。除了不好的地方是使用我不想使用的 Add 方法,因为它打破了域模型控制自己的数据的整个想法。

所以我不确定我的领域模型是否错误,或者这个逻辑是否应该放在服务中,因为它是两个聚合根之间的交互。

角色组Class:

    public bool Archived { get; private set; }

    public int Id { get; private set; }

    public string Name { get; private set; }

    public virtual IList<Role> Roles { get; private set; }

    public virtual IList<User> Users { get; private set; }

更新存档方法:

    private void UpdateArchived(bool archived)
    {
        if (archived && !CanArchive())
        {
            throw new InvalidOperationException("Role Group can not be archvied.");
        }

        Archived = archived;
    }

检查角色组是否可以存档的方法

    private bool CanArchive()
    {
        if (Users.Count > 0)
        {
            return false;
        }

        return true;
    }

在用户中设置用户角色组的方法class 在用户界面中创建或更新用户时调用此方法。

    private void UpdateRoleGroup(RoleGroup roleGroup)
    {
        if (roleGroup == null)
        {
            throw new ArgumentNullException("roleGroup", "Role Group can not be null.");
        }

        RoleGroup = roleGroup;
    }

What I am struggling with is I can not test the CanArchive method below because I have no way off adding in a User to the property.

从数据库加载 RoleGroup 实例的框架如何填充用户?这是您必须问自己的问题,以便为您的单元测试找到解决方案。照着做就行了。

我不知道你用的是什么语言。例如,在 Java 中,您可以使用反射 api 来设置私有字段。还有很多测试框架为这项工作提供了方便的方法,例如Deencapsulation or Whitebox.

一些想法:

  • 域对象的单元测试不应该依赖于持久层的东西。一旦这样做,您就会进行集成测试。

  • 通过数据库整合两个聚合之间的变化在DDD中理论上不是一个好主意。由 User.UpdateRoleGroup() 引起的更改应该保留在 User 聚合中,或者触发其他聚合上的 public 域方法来改变它们(以最终一致的方式)。如果这些方法是 public,它们也应该可以从测试中访问。

  • 使用 Entity Framework、none 确实很重要,因为它不擅长建模 read-only 集合,您很可能会有一个可变的用户列表.我不认为调用 roleGroup.Users.Add(...) 在测试中设置数据是一个大问题,即使您不应该在生产代码中这样做。也许将 internalinternalsVisibleTo 你的测试项目一起制作列表 - 正如@NikolaiDante 建议的那样 - 并将其包装到 public 只读集合中会使它的危险性降低一些。