3

我看过以下问题,它与我的不同:

jMockit:如何期望构造函数调用模拟对象?

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

如何使用 JMockit 模拟 Date 类的默认构造函数?

我想做的是模拟对 的构造函数调用java.util.zip.ZipFile,特别是有java.io.File参数的调用。我希望构造函数返回一个不同的实例ZipFile,我将使用只接受一个String参数的构造函数进行实例化。

这个构造函数调用发生在被测方法中,所以我不能注入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;
    }
};

但这不会像教程中的这一行所示那样工作:constructors have void return type, so it makes no sense to record return values for them

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

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 文件。

我想我可以嘲笑一切会做的ZipFile事情,但这很快就会变成一个巨大的痛苦,因为它被称为很多地方,它的输出需要被嘲笑等等。重构试图使其易于访问会很尴尬,因为使用 的代码ZipFile是代码内部的,公共方法并不真正关心它。

我有一种感觉 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() {行。

我真的想说“与其模拟这个对象,不如替换这个相同类型的另一个对象”。也许我表达得不好。

EDIT2:我想我应该包括版本号:Using Eclipse 3.6.1 Java 1.6.0_26 JMockit 0.999.10

4

2 回答 2

1

JMockit 可以模拟ZipFile类,但它会干扰类加载,因为JarFileJVM 一直使用子类(每当它从类路径中的 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。相反,请使用真实文件。

于 2011-12-06T15:02:20.917 回答
-3

这可能对您没有帮助,但如果您使用的是 Mockito 或 EasyMock,您可以添加 PowerMock,它允许您在被测代码中模拟新对象的构造。

于 2011-12-06T16:30:29.067 回答