如何在Java中模拟静态方法?

4
我有一个名为FileGenerator的类,我正在为generateFile()方法编写测试,它应该执行以下步骤:
1) 它应该调用BlockAbstractFactory上的静态方法getBlockImpl(FileTypeEnum) 2) 它应该通过子类方法getBlocks()填充变量blockList 3) 它应该调用来自最终帮助程序类FileHelper的静态方法createFile,并传递一个字符串参数
4) 它应该调用blockList中每个BlockController的运行方法。
到目前为止,我的方法为空:
public class FileGenerator {
    // private fields with Getters and Setters

    public void generateBlocks() {
    }
}

我正在使用JUnit,Mockito来模拟对象,并尝试使用PowerMockito来模拟静态和final类(Mockito无法做到这一点)。
我的问题是:即使在generateBlocks()中没有实现任何内容,在第一个测试中(调用BlockAbstractFactory中的getBlockList()方法)也通过了。 我已经在BlockAbstractFactory中实现了静态方法(到目前为止返回null),以避免Eclipse语法错误。
如何测试是否在fileGerator.generateBlocks()中调用了静态方法?
以下是我的测试类目前的代码:
@RunWith(PowerMockRunner.class)
public class testFileGenerator {
    FileGenerator fileGenerator = new FileGenerator();

    @Test
    public void shouldCallGetBlockList() {
            fileGenerator.setFileType(FileTypeEnum.SPED_FISCAL);

            fileGenerator.generateBlocks();

            PowerMockito.mockStatic(BlockAbstractFactory.class);
            PowerMockito.verifyStatic();
            BlockAbstractFactory.getBlockImpl(fileGenerator.getFileType());
    }
}

8
抽象方法不能是静态的。 - Sled
3
如果你决定要采用测试驱动开发(TDD)的话,最简单的做法就是摆脱编写静态方法的习惯 :) - Affe
@ArtB 这是一个来自抽象类的静态方法,而不是一个抽象的静态方法。编辑:刚看到我问题中的错误,已经修正了。 - Tarek
@Affe 好吧,如果真的没有其他办法,我会改变它... - Tarek
1
这不是对你问题的回答,只是一个提示:在你的测试方法或者@Before中实例化你的FileGenerator fileGenerator = new FileGenerator();,而不是作为你测试类的成员。使用你当前的实现方式,你将会在你的测试类中共享已经实例化的fileGenerator(假设你也想要独立的测试)。 - bas
@user1042273,您可以发布您想要模拟的BlockAbstractFactory中的静态方法吗? - bas
2个回答

6

我对PowerMock没有经验,但是既然你还没有得到答案,我刚刚阅读了文档,希望能帮助你一些。

我发现需要准备PowerMock,以便它知道需要准备哪些静态方法来进行模拟。就像这样:

@RunWith(PowerMockRunner.class)
@PrepareForTest(BlockAbstractFactory.class) // <<=== Like that
public class testFileGenerator {
    // rest of you class
}
这里 你可以找到更多相关信息。

有帮助吗?


谢谢!当我添加了@PrepareForTest并且我的测试类继承了TestCase时,它起作用了!以下是完整的解决方案:@RunWith(PowerMockRunner.class) @PrepareForTest(BlockAbstractFactory.class) public class testFileGenerator extends TestCase { // 与您的@Before和@After建议相同的代码 } - Tarek
@user1042273 嘿,不错!不过要记住Affe的评论。使用TDD时,静态方法会很麻烦。请谨慎使用。如果您正在向静态类/公共静态方法添加状态,则在进行TDD时会遇到困难。您会克服这些问题的,感谢您的提问,我从未听说过可以处理静态类的模拟框架 :)。我会点赞的。祝好! - bas
1
另一个需要考虑的问题是,模拟静态方法需要在运行时进行字节码操作,并且会带来性能成本。如果您只模拟一个或两个静态方法,那么可能还好,但如果经常这样做,就会真正减慢测试速度。 - bcarlso
@bcarlso 难道不是每个其他的模拟工具都需要通过CGLIB创建新的子类,这也需要在运行时进行字节码操作吗?我真的看不出有什么区别... - Rogério
@Rogerio,也许它确实如此。当模拟标准方法与静态方法时,我在性能方面有了显着的差异体验。我不知道具体细节,所以这可能不是字节码操作。感谢您纠正我。我需要更深入地研究这些差异。 - bcarlso

1

工作示例:

@RunWith(PowerMockRunner.class)
@PrepareForTest({ClassStaticA.class, ClassStaticB.class})
public class ClassStaticMethodsTest {

    @Test
    public void testMockStaticMethod() {
        PowerMock.mockStatic(ClassStaticA.class);
        EasyMock.expect(ClassStaticA.getMessageStaticMethod()).andReturn("mocked message");
        PowerMock.replay(ClassStaticA.class);
        assertEquals("mocked message", ClassStaticA.getMessageStaticMethod());
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接