【问题标题】:Mocking locally created objects in java using Mockito2使用 Mockito2 在 java 中模拟本地创建的对象
【发布时间】:2018-06-27 02:24:14
【问题描述】:

我正在使用 testng 和 Mockito2 为一个项目编写模块测试。我想模拟一些发出出站请求的方法。现在,要模拟的对象是在另一个对象的方法中本地创建的。所以,如果我说 4 个类,A、B、C 和 D,这样 A 创建 B 类型的对象,B 创建 C 类型的对象,依此类推,D 类型的对象要被模拟,我看到我有两个选择来模拟它。

选项 1 是监视 A、B、C 类型的对象,并将 B 的 spy 注入 A 并将 C 注入 B,最后在对象创建期间将 D 的 mock 注入 C。下面是一个例子。

class A {
        public B createB()
        {
            retrun new B();
        }
        public void someMethod ()
        {
            B b = createB();
        }
    }

通过这种方式,我可以在调用 createB 时监视 A 并为 B 注入模拟对象。这样我最终可以模拟 D。

选项 2 是不模拟间歇性课程,而是直接拥有一个工厂课程,如下所示:

class DFactory {
    private static D d;
    static public void setD (D newD)
    {
         d = newD;
    }
    public static D getD()
    {
        if (d!=null)
        {
            return d;
        } else
        {
            return new D();
        }
    }
}

上述选项很简单,但我不确定这是否正确,因为它会创建更多静态方法,我认为应该避免这种情况。

我想知道应该首选哪种方法以及是否有其他替代方法。

请注意,我不希望使用 powermockito 或任何其他鼓励不良代码设计的框架。我想坚持使用mockito2。我可以重构我的代码以使其更具可测试性。

【问题讨论】:

标签: java unit-testing mockito static-methods testability


【解决方案1】:

您现在拥有它的方式,A 创建 B,B 创建 C,C 创建 D,所有这些创建都是 您无法查看或更改的实现细节,特别是 创建依赖对象.

您非常钦佩地避免使用 PowerMockito,并且您也非常有兴趣重构代码以很好地处理此更改,这意味着 将 D 的选择委托给 A 的创建者。尽管我理解您的意思只是让这种选择发生在测试中,但语言并不知道这一点;您正在为依赖项选择不同的实现,并从 C 的实现中选择。这称为控制反转,或依赖注入。 (您可能以前听说过它们,但我在最后介绍这些术语,因为它们通常与当前对话中并不真正需要的重量和框架相关联。)

这有点棘手,因为看起来您不仅需要 D 的实现,还需要创建 D 的新实现。这使事情变得有点困难,但难度不大,尤其是如果您能够使用 Java 8 lambda 和方法引用。在下面的任何地方,您都会看到对 D::new 的引用,这是对 D 的构造函数 that could be accepted as a Supplier<D> parametermethod reference

我会通过以下方式之一重组你的班级:

  • new A() 一样构造A,但在实际调用A 时控制D 的实现,例如aInstance.doSomething(new D())aInstance.doSomething(D::new)。这意味着每次调用方法时,C 都会委托给调用者,从而为调用者提供更多控制权。当然,您可以选择提供内部调用aInstance.doSomething(new D())aInstance.doSomething() 重载,以简化默认情况。
  • new A(D::new)一样构造A,其中A调用new B(dSupplier),B调用new C(dSupplier)。这使得在单元测试中替换 B 和 C 变得更加困难,但如果唯一可能的更改是让网络堆栈由 D 表示,那么您只是根据用例的需要更改代码。
  • new A(new B(new C(D::new)))一样构造A。这意味着 A 只涉及其直接合作者 B,并且更容易将 B 的任何实现替换到 A 的单元测试中。这假设 A 只需要 B 的单个实例而不需要创建它,这可能不是一个好的假设;如果所有类都需要创建它们的子类的新实例,A 将接受Supplier<B>,A 的构造看起来像new A(() -> new B(() -> new C(D::new)))。这是紧凑但复杂的,您可能会选择创建一个 AFactory 类来管理 A 的创建及其依赖项的配置。

如果第三个选项对您很有吸引力,并且您认为您可能希望自动生成像 AFactory 这样的类,请考虑研究像 GuiceDagger 这样的依赖注入框架

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-04-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-02-26
    • 2019-12-24
    相关资源
    最近更新 更多