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

I've looked at the following question and it is not the same as mine:

This question is similar but the answer is not helpful to me:

What I am trying to do is mock a constructor call to java.util.zip.ZipFile, specifically the one that has a java.io.File argument. I would like for the constructor to return an instance of a different ZipFile, one I will instantiate with the constructor that only takes a String argument.

This constructor call takes place inside a method under test, so I can't inject the ZipFile I want as a parameter.

For example, the code looks something like this:

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

}

My first thought was to do the following with static partial mocking:

final ZipFile test = new ZipFile("path/to/actual.zip");

new NonStrictExpectations() {

@Mocked("(java.io.File)")

ZipFile zf;

{

new ZipFile((File) any); result = test;

}

};

But this won't work as indicated by this line in the tutorial: constructors have void return type, so it makes no sense to record return values for them

My second thought was to try the following:

new NonStrictExpectations() {

{

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

}

};

But this throws the following when trying to initialize the file:

java.util.zip.ZipException: error in opening zip file

at java.util.zip.ZipFile.open(Native Method)

at java.util.zip.ZipFile.(Unknown Source)

at java.util.zip.ZipFile.(Unknown Source)

My third thought was to use a @MockClass as below:

@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

}

}

But this hoses some other mocks I have that load a configuration file for a different part of my test class. Not to mention I will want different zip files for different test cases.

I suppose I could mocking everything the ZipFile would do, but this would quickly become a giant pain as it's called lots of places, it's output would be need to be mocked, etc, etc. Refactoring to try to make this accessible would be awkward, as the code that uses the ZipFile is internal to the code and the public methods don't really care about it.

I have a feeling there is a way for JMockit to allow this (giving a particular instance of an object when a constructor is called), but I can't figure it out. Does anyone have any ideas?

EDIT: I tried the method suggested by @Rogerio, but I have a new error. Here's my setup:

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);

}

};

}

};

but I get the following stack trace:

java.lang.InternalError

at path.to.test.ExtractDataTest$1.(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)

where line 61 is the new NonStrictExpectations() { line.

I really want to say "instead of mocking this object, substitute this other object of the same type". Maybe I have expressed that poorly.

EDIT2: I figured I should include version numbers:

Using Eclipse 3.6.1

Java 1.6.0_26

JMockit 0.999.10

解决方案

JMockit can mock the ZipFile class, but it interferes with class loading since the JarFile subclass is used by the JVM all the time (whenever it loads a class from a jar file in the classpath). Currently, there is no easy way to avoid this interference (there is a plan to "fix" this, but it will take time).

However, this particular test case isn't very suited for a mocking tool anyway. Instead, I would recommend setting up the test so that it provides an actual zip file with the desired contents in the proper place.

(another edit)

I just applied a change to JMockit (for release 0.999.12) which allows the following test to pass, provided there is a test.zip file in the working dir, and it contains a text file whose first line is "test":

@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);

}

However, I would still recommend not using a mocking API for cases like this. Instead, use a real file.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值