使用 JMockit 模拟会在内部 类 的局部变量上抛出 NullPointerException
Mocking with JMockit throws NullPointerException on local variables of internal classes
我正在尝试模拟一个方法,该方法创建一个局部变量,尝试一些事情,并在抛出异常时进行记录。这是一段代码:
public void myFunction() {
//Some stuff
try {
new File("foo").getAbsoluteFile();
} catch (SecurityException e) {
//Do some logging
}
}
我想使用 JMockit(如果重要的话使用 1.8 版)来模拟这种日志记录行为。所以我创建了以下测试:
@Test(expected=SecurityException.class)
public void testAbsoluteFile(
@Injectable final File file
) throws IOException {
new Expectations(File.class){{
new File(anyString);
result = file;
file.getAbsoluteFile();
result = new SecurityException();
}};
myFunction();
}
麻烦的是,这似乎让我对 File.getAbsoluteFile()
的内部运作有了一个 NullPointerException
,我觉得这绝对很奇怪:
java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NullPointerException>
Caused by: java.lang.NullPointerException
at java.io.Win32FileSystem.slashify(Win32FileSystem.java:56)
at java.io.Win32FileSystem.resolve(Win32FileSystem.java:330)
at java.io.File.getAbsolutePath(File.java:556)
at java.io.File.getAbsoluteFile(File.java:572)
at com.vue.rescoreservice.ExpectationTest.myFunction(ExpectationTest.java:39)
at com.vue.rescoreservice.ExpectationTest.testAbsoluteFile(ExpectationTest.java:33)
这看起来真的很奇怪,因为它说 Win32FileSystem
class 中的局部变量(内部 class 不是典型的 Java API ) 正在抛出 NullPointerException,而它以前从未这样做过。
堆栈跟踪中的行如下:
//In myFunction()
new File("foo").getAbsoluteFile();
和
//In testAbsoluteFile()
myFunction();
为什么会这样?我怎样才能使 JMockit 不会在内部 classes?
的局部变量上抛出 NullPointerException
该问题已在最新的 jMockit 版本 (1.14) 中修复。如果您现在不想迁移,可以在 1.8 中修复测试(请参见下面的代码)。
在这种情况下,@Injectable 不是必需的。 File 的构造函数针对 Expectations 进行了模拟,这需要模拟 class 本身而不是单个实例。在这种情况下,行为等同于 @Mocked(但 File 将根据 Expectations 块中的调用进行部分模拟)。
myFunction 中的 catch 子句需要重新抛出 SecurityException 才能让测试通过。
@Test(expected=SecurityException.class)
public void testAbsoluteFile() throws IOException {
new Expectations(File.class) {{
File file = new File(anyString);
file.getAbsoluteFile();
result = new SecurityException();
}};
myFunction();
}
如果您更愿意将 mock 声明为参数,它也可以工作,但不会部分模拟 File(将模拟所有方法)。
@Test(expected=SecurityException.class)
public void testAbsoluteFile(@Mocked final File file) throws IOException {
new Expectations(){{
new File(anyString);
file.getAbsoluteFile();
result = new SecurityException();
}};
myFunction();
}
关于@Marc-André 回答的更新。
在我使用 JMockit 1.25 的情况下,我必须在预期之外定义文件变量,让它像这样:
@Test(expected=SecurityException.class)
public void testAbsoluteFile() throws IOException {
File file = new File("");
new Expectations(File.class) {{
file.getAbsoluteFile();
result = new SecurityException();
}};
myFunction();
}
也许是在回答之前对 JMockit 进行了更改。
我正在尝试模拟一个方法,该方法创建一个局部变量,尝试一些事情,并在抛出异常时进行记录。这是一段代码:
public void myFunction() {
//Some stuff
try {
new File("foo").getAbsoluteFile();
} catch (SecurityException e) {
//Do some logging
}
}
我想使用 JMockit(如果重要的话使用 1.8 版)来模拟这种日志记录行为。所以我创建了以下测试:
@Test(expected=SecurityException.class)
public void testAbsoluteFile(
@Injectable final File file
) throws IOException {
new Expectations(File.class){{
new File(anyString);
result = file;
file.getAbsoluteFile();
result = new SecurityException();
}};
myFunction();
}
麻烦的是,这似乎让我对 File.getAbsoluteFile()
的内部运作有了一个 NullPointerException
,我觉得这绝对很奇怪:
java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NullPointerException>
Caused by: java.lang.NullPointerException
at java.io.Win32FileSystem.slashify(Win32FileSystem.java:56)
at java.io.Win32FileSystem.resolve(Win32FileSystem.java:330)
at java.io.File.getAbsolutePath(File.java:556)
at java.io.File.getAbsoluteFile(File.java:572)
at com.vue.rescoreservice.ExpectationTest.myFunction(ExpectationTest.java:39)
at com.vue.rescoreservice.ExpectationTest.testAbsoluteFile(ExpectationTest.java:33)
这看起来真的很奇怪,因为它说 Win32FileSystem
class 中的局部变量(内部 class 不是典型的 Java API ) 正在抛出 NullPointerException,而它以前从未这样做过。
堆栈跟踪中的行如下:
//In myFunction()
new File("foo").getAbsoluteFile();
和
//In testAbsoluteFile()
myFunction();
为什么会这样?我怎样才能使 JMockit 不会在内部 classes?
的局部变量上抛出 NullPointerException该问题已在最新的 jMockit 版本 (1.14) 中修复。如果您现在不想迁移,可以在 1.8 中修复测试(请参见下面的代码)。
在这种情况下,@Injectable 不是必需的。 File 的构造函数针对 Expectations 进行了模拟,这需要模拟 class 本身而不是单个实例。在这种情况下,行为等同于 @Mocked(但 File 将根据 Expectations 块中的调用进行部分模拟)。
myFunction 中的 catch 子句需要重新抛出 SecurityException 才能让测试通过。
@Test(expected=SecurityException.class)
public void testAbsoluteFile() throws IOException {
new Expectations(File.class) {{
File file = new File(anyString);
file.getAbsoluteFile();
result = new SecurityException();
}};
myFunction();
}
如果您更愿意将 mock 声明为参数,它也可以工作,但不会部分模拟 File(将模拟所有方法)。
@Test(expected=SecurityException.class)
public void testAbsoluteFile(@Mocked final File file) throws IOException {
new Expectations(){{
new File(anyString);
file.getAbsoluteFile();
result = new SecurityException();
}};
myFunction();
}
关于@Marc-André 回答的更新。
在我使用 JMockit 1.25 的情况下,我必须在预期之外定义文件变量,让它像这样:
@Test(expected=SecurityException.class)
public void testAbsoluteFile() throws IOException {
File file = new File("");
new Expectations(File.class) {{
file.getAbsoluteFile();
result = new SecurityException();
}};
myFunction();
}
也许是在回答之前对 JMockit 进行了更改。