java - 使用 JMockit 从模拟构造函数返回实际实例

标签 java junit jmockit

我查看了以下问题,它与我的不一样:

jMockit: How to expect constructor calls to Mocked objects?

这个问题很相似,但答案对我没有帮助:

How to mock the default constructor of the Date class with JMockit?

我想做的是模拟对 java.util.zip.ZipFile 的构造函数调用,特别是具有 java.io.File 参数的构造函数。我希望构造函数返回另一个 ZipFile 的实例,我将使用仅采用 String 参数的构造函数实例化该实例。

此构造函数调用发生在被测方法内部,因此我无法注入(inject)我想要的 ZipFile 作为参数。

例如,代码如下所示:

public void whatever() {
   //some code
   //some more code
   foo();
   //yet more unrelated code
}

private Blah foo() {
    ZipFile zf;
    //a bunch of code we don't care about

    zf = new ZipFile(someFile);// I want to give it a known zipfile! mock this!


    // some more code we don't care about

    Enumeration<?> entries = zf.entries();
    ZipEntry entry = (ZipEntry) entries.nextElement();
    InputStream is = zf.getInputStream(entry)
    //maybe some other calls to the ZipFile

    // do something else
}

我的第一个想法是通过静态部分模拟执行以下操作:

final ZipFile test = new ZipFile("path/to/actual.zip");
new NonStrictExpectations() {
    @Mocked("(java.io.File)")
    ZipFile zf;
    {
        new ZipFile((File) any); result = test;
    }
};

但这不会起作用,如教程中的这一行所示:构造函数具有 void 返回类型,因此为它们记录返回值是没有意义的

我的第二个想法是尝试以下操作:

new NonStrictExpectations() {
    {
        newInstance("java.util.zip.ZipFile", new File("path/to/actual.zip"));
    }
};

但是在尝试初始化文件时会抛出以下错误:

java.util.zip.ZipException: error in opening zip file
at java.util.zip.ZipFile.open(Native Method)
at java.util.zip.ZipFile.<init>(Unknown Source)
at java.util.zip.ZipFile.<init>(Unknown Source)

我的第三个想法是使用 @MockClass ,如下所示:

@Before
public void setUp() throws Exception {
    Mockit.setUpMocks(MockedZipFile.class);
}
@After
public void tearDown() {
    Mockit.tearDownMocks();
}

@MockClass(realClass=ZipFile.class)
public static class MockedZipFile {
    public ZipFile it;
    @Mock
    public void $init(File f) throws ZipException, IOException {
        it = new ZipFile("path/to/actual.zip");//this is what would be called
    }
}

但这会影响我的其他一些模拟,这些模拟会为我的测试类的不同部分加载配置文件。更不用说我需要针对不同的测试用例使用不同的 zip 文件。

我想我可以 mock ZipFile会做的一切,但这很快就会变成一个巨大的痛苦,因为它在很多地方被调用,它的输出需要尝试进行重构以使其可访问会很尴尬,因为使用 ZipFile 的代码是代码内部的,并且公共(public)方法并不真正关心它。

我有一种感觉,JMockit 有一种方法可以允许这样做(在调用构造函数时给出对象的特定实例),但我无法弄清楚。有人有什么想法吗?

编辑:我尝试了@Rogerio建议的方法,但出现了新错误。这是我的设置:

final ZipFile test = new ZipFile("path/to/actual.zip");
new NonStrictExpectations() {
    ZipFile zf;
    {
        zf.entries();
        result = test.entries();
        zf.getInputStream((ZipEntry) any);
        result = new Delegate() {
            InputStream getInputStream(ZipEntry entry) throws IOException {                 
                return test.getInputStream(entry);
            }
        };
    }
};

但我得到以下堆栈跟踪:

java.lang.InternalError
at path.to.test.ExtractDataTest$1.<init>(ExtractDataTest.java:61)
at path.to.test.ExtractDataTest.setUp(ExtractDataTest.java:61)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:49)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

其中第 61 行是 new NonStrictExpectations() { 行。

我真的想说“不要 mock 这个对象,而是替换相同类型的另一个对象”。也许我表达得不好。

EDIT2:我想我应该包含版本号: 使用Eclipse 3.6.1 Java 1.6.0_26 JMockit 0.999.10

最佳答案

JMockit 可以模拟 ZipFile 类,但它会干扰类加载,因为 JarFile 子类始终被 JVM 使用(每当它从类路径中的 jar 文件)。目前,没有简单的方法可以避免这种干扰(有计划“修复”此问题,但这需要时间)。

但是,这个特定的测试用例无论如何都不太适合模拟工具。相反,我建议设置测试,以便它在适当的位置提供包含所需内容的实际 zi​​p 文件。

(另一编辑) 我刚刚对 JMockit(版本 0.999.12)进行了更改,允许以下测试通过,前提是工作目录中有一个 test.zip 文件,并且它包含一个文本文件,其第一个行是“测试”:

@Test
public void mockZipFile() throws Exception
{
    final ZipFile testZip = new ZipFile("test.zip");

    new NonStrictExpectations() {
        @Capturing @Injectable ZipFile mock;

        {
            mock.entries(); result = testZip.entries();

            mock.getInputStream((ZipEntry) any);
            result = new Delegate() {
                InputStream delegate(ZipEntry e) throws IOException {
                    return testZip.getInputStream(e);
                }
            };
        }
    };

    ZipFile zf = new ZipFile("non-existing");
    ZipEntry firstEntry = zf.entries().nextElement();
    InputStream content = zf.getInputStream(firstEntry);
    String textContent = new BufferedReader(new InputStreamReader(content)).readLine();

    assertEquals("test", textContent);
}

但是,我仍然建议不要在此类情况下使用模拟 API。相反,请使用真实的文件。

关于java - 使用 JMockit 从模拟构造函数返回实际实例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8389561/

相关文章:

java - Jmockit 不会合并使用coverage-output=serial 生成的两个coverage.ser 文件

java - 当模拟类包含返回 Integer 的方法时 jmockit 问题

java - 一起使用 JMockit 和 Spring AOP

java - 尝试使用 Amazon Product Affiliate API 时出错

Java - OnClickListener 中的最终变量要求

java - assertEquals(Object o1, Object o2) 是否使用 equals 方法

java - 使用 SpringJunit4ClassRunner 在属性占位符之前设置系统属性或环境变量

java - 无版本 gradle 依赖项

java - 匹配字符串之间的多个数字,处理并替换

java - 使用 ant 在两个 jvm 中并行运行 junit