【问题标题】:How to inject spy object into private field如何将间谍对象注入私有字段
【发布时间】:2019-09-10 12:22:29
【问题描述】:

我正在尝试将间谍注入私有字段private Map<Integer, IPatron> patrons;,但没有成功。该字段不属于任何构造函数,也不具有任何 setter 或 getter。如果可能,我会尽量避免更改源代码。

如果该字段设置为公共,我可以显式分配间谍,但据我了解它应该自动发生。


package library;

import java.util.Map;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Spy;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;

import library.entities.IPatron;
import library.entities.Library;


@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT) // allows unnecessary "stubbing"
class TestTing{
    @Spy 
    private Map<Integer, IPatron> patrons;

    @Spy
    @InjectMocks
    Library library;

    @BeforeEach
    void setUp(){
    }

    @Test
    void getPatronList(){
        library.getPatronList();
        //someTest
        patrons.size();
    }
}



    private Map<Integer, IPatron> patrons;


    public Library() {

        patrons = new HashMap<>();
    }

    @Override
    public List<IPatron> getPatronList() {      
        return new ArrayList<IPatron>(patrons.values()); 
    }

我的理解是@Spy private Map&lt;Integer, IPatron&gt; patrons;会在被测类中找到private Map&lt;Integer, IPatron&gt; patrons;并替换掉。

【问题讨论】:

  • 为什么你认为它不起作用?
  • @second 在运行时检查该字段会显示一个普通的 HashMap 对象,而不是模拟注入的 HashMap。
  • 你用的是什么mockito版本?当我使用 v3.0.0 验证这一点时,调试显示模拟已正确注入,并且您的测试的(更完整)版本返回了我为模拟指定的结果。 -- 无论如何做@MarkBramnik 建议的改变是更干净的方式。

标签: java mockito


【解决方案1】:

首先,您正在测试类 Library 对,所以它不是间谍或任何东西,它只是一个常规实例,因此您应该像这样在测试中创建它:

 Library underTest = new Library(...);

现在,您的问题的答案不是关于 Mockito,而是关于使代码可单元测试。

并非任何生成的代码都可以自动进行单元测试。

由于Library 类现在是不可能写的(除非你用反射强行设置顾客)。反射也不是这样做的好方法。

我建议使用依赖注入原理,像这样重构代码:

class Library {
   private Map<Integer, IPatron> patrons;

   public Library(Map<Integer, IPatron> patrons) {
        this.patrons = patrons;
   }
}

然后在测试中,您可以将patrons 创建为地图、间谍、模拟或任何您想模拟真实交互并注入Library

Library underTest = new Library(patrons);

【讨论】:

  • 虽然我同意您的建议,但我只想指出,在执行问题中的代码时,间谍已正确放置。无论无参数构造函数在做什么都不会妨碍Field injection。 -- 此外,一般地图不应被模拟或窥探,应尽可能使用真实地图。
  • 感谢您的回复。我想我把自己逼到一个角落里,试图变得纯洁。我与我的讲师交谈,他提到需要对源进行一些更改才能对其进行测试。
【解决方案2】:

您在 Library 类中硬编码赞助人依赖项。不要使用硬编码。
我们应该请求依赖而不是自己定义它们。
我们要求构造函数中的强制依赖项和 setter 中的可选依赖项。
将构造函数的签名更改为以下内容:

public Library(Map<Integer, IPatron> patrons) {
    this.patrons = patrons;
}

还让赞助人进入决赛。

在这种情况下,图书馆类并不关心顾客是如何创建的以及它是什么。因为这不是它的责任。

您现在不需要任何间谍或模拟来测试库类。而且它更具可扩展性和可测试性。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多