【问题标题】:Initializing a mock object internals before injecting it with @InjectMocks在使用 @InjectMocks 注入之前初始化模拟对象内部
【发布时间】:2018-07-18 06:37:51
【问题描述】:

我有类似的东西

private static final CustomObject ObjectA = new CustomObject();

    @Mock
    Foo1 foo1;

    @Mock
    Foo2 Foo2 = new Foo2(ObjectA);

    @Mock
    Foo3 foo3;

    @InjectMocks
    ContainerClass container;

我想在 Foo2 被注入容器之前用 ObjectA 初始化它。上面的代码不起作用。

编辑:我正在尝试模拟 Foo2,但有一个 Foo2 的内部对象,我想用一个真实对象初始化它,所以当我调用 Foo2 的方法时,这个内部对象被用来给我基于我需要的结果我在构建过程中提供的值。

【问题讨论】:

  • 当前状态下的问题不完整,因此不清楚。阅读How to Ask,然后阅读edit 问题,提供minimal reproducible example,可用于重现问题,从而更好地理解所问的问题。
  • 如果你在模拟 Foo2,那么当你调用 Foo2 的方法时,结果不能由它的构造方式来定义。您需要使用 Mockito.when 定义结果

标签: java unit-testing dependency-injection mockito


【解决方案1】:

上面的代码不起作用。

1) @InjectMocks 使用了很多“魔法”,并不是设置被测对象模拟的最清晰和可调试的方法。
在幕后,它尝试了多种事情:构造函数注入、属性设置器注入、字段注入。
但是如果注入失败,则不会报告失败:

如果以下任何策略失败,则 Mockito 不会报告 失败;即您必须自己提供依赖项。

作为替代方案,您可以明确设置被测对象的依赖关系。

2) 附带但重要的注意事项:用小写字母作为第一个字母命名变量。用类名命名它们既不可读也不传统。

3) 这没有意义:

@Mock
Foo2 Foo2 = new Foo2(ObjectA);

您实例化 Foo2,然后将其替换为 Mockito 模拟。
模拟 Foo2 行为但让其 ObjectA 依赖项成为非模拟对象并不是真正的逻辑。
通过这样做,您不会真正模拟被测对象的依赖关系,因为您的调用模式是:

待测对象 -> 模拟 dep1 -> 真实对象 dep2

在这种情况下,编写集成测试(没有模拟)更有意义。

当您对ContainerClass 进行单元测试时,您将只测试ContainerClass 的行为。
您希望获得结果的ObjectA 方法应该是调用Foo2ObjectA,您可以在测试中模拟对Foo2 的调用。
请注意,如果您模拟其调用者的方法,则无需模拟 ObjectA

这是一个例子:

public class Foo2{

    private ObjectA objectA;

    public Foo2(ObjectA objectA){
       this.objectA = objectA;
    }

    public Bar callObjectA(){
         return objectA.foo();
    }
}

你需要在这里模拟的是callToObjectA()

@Mock
Foo1 foo1;

@Mock
Foo2 foo2;

@Test
public void myMethod(){
   ContainerClass container = new ContainerClass(foo1, foo2);
   Bar mockedBar = new Bar(....);
   Mockito.when(foo2.callObjectA()).thenReturn(mockedBar);
   // invoke the method under test
   container.myMethod();
   // assertions ...
}

【讨论】:

  • 那么是否可以先模拟 ObjectA,然后将其注入 Foo2,最后将所有内容注入 ContainerClass?
  • 你可以做得更简单。如果您模拟其调用者的方法,则不需要模拟ObjectAFoo2
  • 调用者的方法?实际上,当我调用访问 ObjectA 的特定方法时,我得到了一个空异常,所以在我看来只是在做一个 @Mock Foo2 foo2;没有初始化 Foo2 的内部,当容器对象访问该内部对象时,我得到了 null 异常。
  • Foo2 mock 可能没有注入到 ContainerClass 对象中。真正避免@InjectMocks。我更新了一个示例,说明如何通过明确设置依赖项来做到这一点。
【解决方案2】:

尽管有代码风格,但您遇到的问题是 Foo2 并不是真正的模拟,因为您明确地实例化了它。如果您真的想使用用Object2 实例化的“真实”Foo2 实例,请考虑在Foo2 上使用@Spy 注释。然后,@InjectMocks 注释应该符合您的预期。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-07-23
    • 2013-03-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-05
    • 1970-01-01
    相关资源
    最近更新 更多