2

对于一个研究项目,我试图弄清楚单元测试(用 JUnit 编写)在不同的上下文中执行时是否表现不同。为此,我做了两件事:首先,我使用自定义 JUnit Runner 运行程序的整个测试套件,然后使用相同的自定义 JUnit runner 运行单个测试(为什么这有意义并不重要,只需接受它现在)。当测试失败时,我会记录 JUnit 报告的整个异常堆栈跟踪,然后比较两次运行之间的堆栈跟踪。

这样做,我偶然发现了一些我无法解释的奇怪东西。以下是以这种方式记录的两个堆栈跟踪的两个摘录。

运行整个测试套件时记录的堆栈跟踪:

org.fest.swing.edt.GuiActionRunner.resultOf(GuiActionRunner.java:126)
org.fest.swing.edt.GuiActionRunner.execute(GuiActionRunner.java:73)
net.sourceforge.squirrel_sql.fw.datasetviewer.cellcomponent.AbstractNumericDataTypeUITest.constructTestFrameInEDT(AbstractNumericDataTypeUITest.java:98)
net.sourceforge.squirrel_sql.fw.datasetviewer.cellcomponent.AbstractNumericDataTypeUITest.setUp(AbstractNumericDataTypeUITest.java:81)
sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source) - sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
java.lang.reflect.Method.invoke(Method.java:616)
org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)

运行单个测试时记录的堆栈跟踪:

org.fest.swing.edt.GuiActionRunner.resultOf(GuiActionRunner.java:126)
org.fest.swing.edt.GuiActionRunner.execute(GuiActionRunner.java:73)
net.sourceforge.squirrel_sql.fw.datasetviewer.cellcomponent.AbstractNumericDataTypeUITest.constructTestFrameInEDT(AbstractNumericDataTypeUITest.java:98)
net.sourceforge.squirrel_sql.fw.datasetviewer.cellcomponent.AbstractNumericDataTypeUITest.setUp(AbstractNumericDataTypeUITest.java:81)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
java.lang.reflect.Method.invoke(Method.java:616)
org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)

两个堆栈跟踪都显示了崩溃前的最后几个条目,从最后一个公共语句开始。显然,在 JDK/VM 的反射实现中的某处,这两种情况的行为有所不同。

我的问题是为什么?

我推测这与早期反射调用中的 VM 缓存内容有关,但我真的不知道。了解这一点很重要,因为我必须弄清楚每次运行单个测试时是否会发生这种情况(因此我可以忽略它),或者这是否与我正在运行的特定测试有关。

我知道这是相当模糊的,但任何帮助将不胜感激。

4

1 回答 1

3

这是因为 Sun/Oracle JVM(其他我不知道)优化了反射调用。从 1.4 开始,JVM 生成字节码以将反射调用桥接到被调用的方法。因为并非所有事情在编译时都是已知的,所以必须在运行时完成。

MethodAccessorGenerator类生成GeneratedMethodAccessor1

我从 NativeMethodAccessorImpl 的评论中发现的一个提示:

Used only for the first few invocations of a Method; afterward, switches to bytecode-based implementation

您是否在一个 Java VM 的生命周期中运行该套件和单个测试?

于 2012-08-20T23:02:32.783 回答