2

这是我的源类 -

public class ClassToTest extends AbstractSuperClass<Integer> {
    private static final ClassToTest INSTANCE = new ClassToTest(); // (line 1) need to mock this variable

    static ClassToTest get() {
        return INSTANCE;
    }
    private ClassToTest() {
        super(Integer.class);// (line 2)
    }
}

这是我迄今为止测试它的尝试

@RunWith(PowerMockRunner.class)
@PrepareForTest(ClassToTest.class)
public class TestClass {
    private ClassToTest testClass;
    @Before
    public void setUp() {
        // each of the below attempts fails at line 1  because of the call to line 2 (annotated above).
        // Attempt A.  
        testClass = WhiteBox.newInstance(ClassToTest.class);
        //Attempt B.
        testClass = mock(ClassToTest.class);
        WhiteBox.setInternalState(ClassToTest.class, "INSTANCE", testClass);
    }
    @Test
    public void dummy() {
        // irrelevant
    }
}

我正在尝试有效地模拟ClassToTest.INSTANCE并调用它的私有构造函数。我怎么能那样做?

编辑:从AbstractSuperClass.

public abstract class AbstractSuperClass<V extends Serializable> {
    private final CacheClient<V> cache;
    private final int seconds;

    public AbstractSuperClass(Class<V> valueType) {
        cache = new ClientFactory(Config.getAppConfig(), StatisticSet.getGlobalStatistics()).newClient(getCacheType(), valueType);
        seconds = Config.getAppConfig().get(getCacheType().getSectionEnum()).getSeconds();
    }

PS:我试图避免处理内部问题,AbstractSuperClass并且最初希望只是简单地模拟调用。我也愿意接受任何重构ClassToTest以避免这种情况的想法。

4

3 回答 3

2

真正的问题是您通过使用static创建了难以测试的、几乎不可测试的代码。不仅如此:你还创造了一个糟糕的设计。因为您将生产类彼此紧密耦合。一旦到位,以后就很难摆脱这种静态方法。

因此,另一种选择:与其尝试使用 PowerMock 来“修复”损坏的设计,不如退后一步。您了解“编写可测试”代码的实际意义(例如通过观看这些视频);然后你使用纯接口和依赖注入来解决你的问题。您可以使用 EasyMock 或 Mockito 测试所有这些,而无需 Powermock!

于 2016-09-11T08:46:25.290 回答
2

我不明白你想要实现什么,但这在这里工作:

@PrepareForTest(ClassToTest.class) // required
@RunWith(PowerMockRunner.class)    // required
public class ClassToTestTest {

    private ClassToTest testClass;

    @Before
    public void setUp() throws Exception {
        this.testClass = Mockito.mock(ClassToTest.class);
        final Field instance = ClassToTest.class.getDeclaredField("INSTANCE");
        instance.setAccessible(true);
        instance.set(null, this.testClass);
    }

    @Test
    public void testGet() throws Exception {
        assertSame(this.testClass, ClassToTest.get());
        System.out.println(this.testClass);
    }
}

输出:

Mock for ClassToTest, hashCode: 1083021083

(使用 powermock-api-mockito 1.6.2 版测试)

于 2016-09-11T09:15:09.713 回答
1

我不相信模拟该字段是正确的方法,我什至不相信它是可能的,因为你不能覆盖一个字段,只有方法可以被覆盖,这实际上是模拟的工作方式。事实上,模拟只是我们覆盖方法的某种代理。

你应该模拟ClassToTest.get()如下:

@RunWith(PowerMockRunner.class)
@PrepareForTest(ClassToTest.class)
public class TestClass {
    private ClassToTest testClass;
    @Before
    public void setUp() {
        testClass = PowerMockito.mock(ClassToTest.class);
        PowerMockito.mockStatic(ClassToTest.class);
        Mockito.when(ClassToTest.get()).thenReturn(testClass);
    }

    @Test
    public void dummy() {
        // Here I get the instance of ClassToTest that I mocked in the setUp method
        System.out.println(ClassToTest.get());
    }
}
于 2016-09-10T18:25:11.933 回答