功能测试中的JMockit + Jetty

功能测试中的JMockit + Jetty

问题描述:

我正在使用ShrinkWrap在集成测试中启动Jetty服务器.

I'm using ShrinkWrap to start Jetty server in my integration tests.

问题:

Problem:

当我启动测试码头服务器并制作控制器模型时,模型不起作用! 我建议原因是不同的类加载器:JMockit-AppClassLoader,Jetty-WebAppClassLoader.

When I start my test jetty-server and than make mockup of my controller - mockup doesn't work! I suggest that the reason is different classloaders: JMockit - AppClassLoader, Jetty - WebAppClassLoader.

问题:

Question:

如何使模拟工作正常?

PS 我用Google搜索 -javaagent:jmockit.jar 选项可能会有所帮助.但事实并非如此.基于1.7 jdk的maven项目是否必要?

P.S. I've googled that -javaagent:jmockit.jar option may help. But it doesn't. Is it necessary for maven project based on 1.7 jdk?

添加:

我写了演示来说明我的问题.您可以通过参考找到它.

I've written demo to illustrate my problem. You can find it by the reference.

关于我的演示:

除了十个代码点之外,它与那些项目相同. 我只添加了JMockit和一个单独的模拟程序来说明问题.

Except of ten stokes of code, it is identical to those project. I've only added JMockit and a single mock to illustrate the problem.

您应该看到 JettyDeploymentIntegrationUnitTestCase.requestWebapp 方法:在这些方法中,我们进行模拟操作不起作用.

You should see JettyDeploymentIntegrationUnitTestCase.requestWebapp method: in those method we make mock which doesn't work.

您可以检查Jetty& JMockit通过同级类加载器加载类,因此JMockit根本看不到Jetty的类

You can check that Jetty & JMockit loads classes by siblings classloaders, so JMockit simply doesn't see Jetty's classes

URLClassLoader
|
|-Launcher$AppClassLoader
|-WebAppClassLoader

示例项目中的JUnit测试正在尝试模拟ForwardingServlet类.但是,在具有嵌入式Jetty Web服务器的这种情况下,实际上有两个此类的实例,它们都在同一JVM中但通过不同的类加载器加载.

The JUnit test in the example project is attempting to mock the ForwardingServlet class. But, in this scenario with an embedded Jetty web server, there are actually two instances of this class, both loaded in the same JVM but through different classloaders.

该类的第一个实例由常规的类加载器加载,通过该类加载器从启动JUnit测试运行程序(AppClassLoader)的线程加载类.因此,当ForwardingServlet出现在测试代码中时,它就是该类加载器中定义的代码.这是提供给JMockit进行模拟的类,这正是发生的情况.

The first instance of the class is loaded by the regular classloader, through which classes are loaded from the thread that starts the JUnit test runner (AppClassLoader). So, when ForwardingServlet appears in test code, it is the one defined in this classloader. This is the class given to JMockit to mock, which is exactly what happens.

但是随后,已将ForwardingServlet副本加载到已部署的Web应用程序内部(从文件系统中的".class"文件加载,因此不受JMockit应用的模拟的影响, (仅在内存中),使用Jetty的WebAppClassLoader. JMockit从未见过此类.

But then, a copy of ForwardingServlet is loaded inside the deployed web app (from the ".class" file in the file system, so not affected by the mocking as applied by JMockit, which is in-memory only), using Jetty's WebAppClassLoader. This class is never seen by JMockit.

有两个可能的解决方案:

There are two possible solutions to this issue:

  1. 以某种方式获取WebAppClassLoader加载的类对象,然后通过调用MockUp(Class)构造函数对其进行模拟.

  1. Somehow get the class object loaded by WebAppClassLoader and then mock it by calling the MockUp(Class) constructor.

配置Jetty服务器,以使其对Web应用程序中的类使用自定义类加载器.

Configure the Jetty server so that it does not use a custom classloader for the classes in the web app.

第二种解决方案是最简单的,可以简单地通过在将处理程序设置到Jetty Server对象中之前,在从WebArchive对象创建的ContextHandler对象上添加以下调用来完成:

The second solution is the easiest, and can be done simply by adding the following call on the ContextHandler object created from the WebArchive object, before setting the handler into the Jetty Server object:

handler.setClassLoader(ClassLoader.getSystemClassLoader());

我对此进行了测试,并按预期方式工作,执行了@Mock doGet(...)方法,而不是ForwardingServlet中的实际方法.

I tested this and it worked as expected, with the @Mock doGet(...) method getting executed instead of the real one in ForwardingServlet.