当它在流中作为方法引用出现时,如何模拟构造函数?

问题描述 投票:2回答:2

我想测试以下类Foo的构造函数。

import java.util.List;
import java.util.stream.Collectors;

public class Foo {
    public List<Bar> bars;

    public Foo(List<Integer> list) {
        bars = list.stream()
                   .map(Bar::new)
                   .collect(Collectors.toList());
    }
}

其中Bar看起来像这样:

public class Bar {
    public Bar(Integer baz) {
        throw new RuntimeException("This crashes, but who cares, I’m testing Foo!");
    }
}

由于我对Bar的实施充满信心,我想通过嘲笑Foo来测试我对Bar::new的实现。

这是我尝试过的:

import java.util.Arrays;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

@RunWith(PowerMockRunner.class)
@PrepareForTest(Foo.class) // where Bar::new is referenced
public class FooTest {
    @Test
    public void testFoo () throws Exception {
        Bar firstBar = PowerMockito.mock(Bar.class);
        Bar secondBar = PowerMockito.mock(Bar.class);

        PowerMockito.whenNew(Bar.class).withArguments(1)
                .thenReturn(firstBar);
        PowerMockito.whenNew(Bar.class).withArguments(2)
                .thenReturn(secondBar);

        Foo actual = new Foo(Arrays.asList(1, 2));

        Assert.assertNotNull(actual);
        Assert.assertNotNull(actual.bars);
        Assert.assertEquals(firstBar, actual.bars.get(0));
        Assert.assertEquals(secondBar, actual.bars.get(1));
    }
}

在这种情况下,调用实际的Bar::new,并且引发RunTimeException。回想起来这是有道理的,因为实际调用new不是在Foo(它会被模拟)中,而是在一些与流相关的匿名类中(我认为)。

如何在不依赖Foo构造函数的实际实现的情况下实现测试Bar的原始目标?

java java-8 junit4 powermock powermockito
2个回答
3
投票

好的,我已经发现这种情况正在发生。表达式Bar::new是一个lambda,PowerMock无法模拟。如果你将它重写为i -> new Bar(i),即使用显式的“new Bar”运算符 - 它可以正常工作。

因此,重点是使用POWERMOCK - 这很糟糕!看到我的另一个答案。


2
投票

使用一个简单的重构来提取Bar工厂,你不需要做任何与powermock hacks有关的事情。

class Foo {
    public List<Bar> bars;
    // existing constructor
    public Foo(List<Integer> list) {
        this(list, Bar::new);
    }
    // constructor which trivial to test.
    public Foo(List<Integer> list, Function<Integer, Bar> barFactory) {
        bars = list.stream()
                .map(barFactory)
                .collect(Collectors.toList());
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.