领域驱动设计 - 可测试性和“新”关键字

时间:2012-11-27 10:14:08

标签: java spring unit-testing domain-driven-design

我一直在尝试在我的新项目中遵循域驱动的设计方法。我一直使用Spring进行依赖注入,这很好地将我的应用程序代码与构造代码分开,然而,使用DDD我似乎总是有一个域对象想要创建另一个域对象,这两个域对象都具有状态和行为。

例如,给定媒体文件,我们希望将其编码为不同的格式 - 媒体资产调用转码服务并接收回调:

class MediaAsset implements TranscodingResultListener {

    private NetworkLocation permanentStorage;
    private Transcoder transcoder;

    public void transcodeTo(Format format){
        transcoder.transcode(this,format);
    }

    public void onSuccessfulTranscode(TranscodeResult result){
        Rendition rendition = new Rendition(this, result.getPath(), result.getFormat());
        rendition.moveTo(permanentStorage);
    }

}

这引发了两个问题:

  • 如果rendition需要一些依赖项(比如MediaAsset需要一个“Transcoder”)并且我想使用类似Spring的东西来注入它们,那么我必须使用AOP来运行我的程序,我不这样做等。
  • 如果我想要MediaAsset的单元测试,测试新格式是否被移动到临时存储,那么我该怎么做?我无法模拟rendition类来验证它的方法是否被调用......将创建真正的Rendition类。

有一个工厂来创建这个类是我考虑过的,但是包含导致问题的“new”关键字需要大量的代码开销。

这里有一种我想念的方法,还是我只是做错了?

2 个答案:

答案 0 :(得分:3)

我认为在这种情况下注入RenditionFactory是正确的方法。我知道它需要额外的工作,但你也从你的班级中删除了SRP违规。在业务逻辑中构建对象通常很诱人,但我的经验是,对象或对象的注入在100次中有99次得到回报。特别是如果所提到的对象是复杂的,和/或如果它与系统资源交互。

答案 1 :(得分:2)

我认为你的单元测试方法是单独测试MediaAsset。这样做,我认为工厂是常见的解决方案。

另一种方法是测试整个系统(或几乎整个系统)。让您的测试访问外部接口[1](用户界面,Web服务接口等),并为系统访问的所有外部系统(数据库,文件系统,外部服务等)创建测试双精度。然后让测试注入这些外部依赖项。

这样做,您可以让测试完全与行为有关。测试与实现细节分离。例如,您可以使用Rendition的依赖注入,而不是:测试不关心。此外,您可能会发现MediaAssetRendition不是正确的概念[2],您可能需要将MediaAsset拆分为两个,并将其中的一半与Rendition合并。同样,你可以做到这一点而不用担心测试。

(免责声明:在外层测试并不总是有效。有时你需要测试常见的概念,这需要你编写微测试。然后你可能会再遇到这个问题。)

[1]最佳级别实际上可能是“域界面”,低于用户界面的级别,您可以使用域语言而不是字符串和整数,以及您可以在哪里谈论域操作而不是按钮点击和焦点事件

[2]也许这实际上是您的问题:MediaAssetRendition是正确的概念吗?如果你问你的域专家,他知道这些是什么吗?如果没有,你真的在​​做DDD吗?