【问题标题】:How to mock property set in constructor如何模拟构造函数中设置的属性
【发布时间】:2016-11-17 15:12:37
【问题描述】:

假设我有一个具有以下构造函数的类:

public class MyImpl extends Abstract<Foo> {

    @Autowired
    private FooClass foo;

    private final ThreadPoolExecutor executor;

    public MyImpl(String name, int num) {
        super(name);
        this.executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(num);

    }

这个类有以下方法:

    @Override
    public void doThis() {
        for (int i = 0; i < num; i++) {
            executor.execute(() -> foo.doMethod());
        }
        executor.shutdown();

        super.doThis();
    }

现在,我想测试 foo.doMethod 是否被调用了 4 次 executor.execute(any())executor.shutdown() 也被调用了 4 次。

目前为止

@RunWith(PowerMockRunner.class)
@PrepareForTest(Executors.class)
public class MyImplTest {

    private static final int NUM = 4;

    @Mock
    private FooClass foo;
    @Mock
    private ThreadPoolExecutor executor;
    @InjectMocks
    private MyImpl imyImpl = new MyImpl("Name", NUM);

    @Test
    public void shouldCallFourTimes() throws Exception {
        PowerMockito.mockStatic(Executors.class);
        when(Executors.newFixedThreadPool(NUM)).thenReturn(foo);

        myImpl.doThis();

        PowerMockito.verifyStatic();
        Executors.newFixedThreadPool(NUM);
        verify(foo, times(NUM)).doMethod());

    }

但是这不起作用。 Mockito 说我的模拟执行器没有任何交互。 由于@Autowired 依赖不是构造函数的一部分,我需要在字段中使用@InjectMocks 指定构造函数。然而,到了我PowerMockito.mockStatic(Executors.class)的时候,MyImpl的构造函数已经通过一个“真实的”Executors.newFixedThreadPool创建了自己的执行器。

知道如何解决这个问题吗?

更新: 显然改变设计没什么大不了的,我现在有以下内容:

public class MyImpl extends Abstract<Foo> {

    @Autowired
    private FooClass foo;

    private final ThreadPoolExecutor executor;

    public MyImpl(String name, ThreadPoolExecutor executor) {
        super(name);
        this.executor = executor;
}

测试:

@Mock
private ThreadPoolExecutor executor;
@InjectMocks
private MyImpl imyImpl = new MyImpl("Name", executor);

但是,executor 在到达构造函数时以某种方式为空。

【问题讨论】:

  • 你不应该用 'new MyImpl("Name", NUM)' 创建新对象,@InjectMocks 会自动实例化一个
  • 如果我不这样做,Mockito 会抱怨 MyImpl 没有默认构造函数。注意FooClass foo没有作为参数传递给构造函数
  • 一些解释将如何得到极大的赞赏
  • 您已经得到了答案(我会建议与 SpaceTrucker 相同)。不管怎样,你可能想看看这些视频来提高你编写可测试代码的技能:youtube.com/playlist?list=PLD0011D00849E1B79

标签: java spring unit-testing mockito powermock


【解决方案1】:

问题在于MyImpl的设计。

与其在构造函​​数中创建线程池执行器,不如将其传递给它。您可以为当前的构造函数放置一个静态工厂方法,这样您仍然可以使用您现在使用的相同参数创建一个实例。

为什么需要ThreadPoolExecutor 的演员表?这使您可以依赖特定的实现。如果你不需要这个,你最好使用java.util.concurrent.ExecutorService

然后你就可以使用普通的 mockito 和普通的 ExecutorService mock。

【讨论】:

  • 是的,我开始认为这是唯一的方法
  • 我现在遇到了其他问题...我更新了问题
  • @user3083022 在这种情况下不要使用@InjectMocks。直接在您的测试用例中创建实例。在调用构造函数时,Mockito 没有机会评估 @Mock 注释。
  • 但是我试图用 @Mock 模拟的依赖项(在这种情况下是 foo)不会被注入到我的 MyImpl 实例中,对吧?
  • @user3083022 正确。然后,您需要通过构造函数传递所有依赖项。根据具体情况,还可以通过依赖注入提供所有依赖。所以你会有一个@Autowire private ExecutorService executor 字段。
【解决方案2】:

我猜问题是你的模拟类没有被注入,

你可以试试这样的,

@Mock
private FooClass foo;
@Mock
private ThreadPoolExecutor executor;

private MyImpl imyImpl;

@Before
public void setUp() throws Exception {
   imyImpl = new MyImpl("Name", NUM);
   impl.setFoo(foo);
   impl.setExecutor(executor);
}

//测试

【讨论】:

  • 我不想让外部设置执行器
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-12-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-21
  • 2023-03-26
  • 2019-11-04
相关资源
最近更新 更多