[英]Testing Google App Engine ThreadManager outside of GAE
我编写了一个JUnit(4.10)单元测试, com.google.appengine.api.ThreadManager
进行了以下调用:
ThreadManager.currentRequestThreadFactory();
当这个测试运行时,我从这个currentRequestThreadFactory
方法中抛出一个NullPointerException
:
Caused by: java.lang.NullPointerException
at com.google.appengine.api.ThreadManager.currentRequestThreadFactory(ThreadManager.java:39)
at com.myapp.server.plumbing.di.BaseModule.providesThreadFactory(BaseModule.java:50)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
当我下载ThreadManager
的源代码并查看第39行(NPE的源代码)时,我看到:
public static ThreadFactory currentRequestThreadFactory() {
return (ThreadFactory) ApiProxy.getCurrentEnvironment().getAttributes()
.get(REQUEST_THREAD_FACTORY_ATTR);
}
因此,似乎ApiProxy.getCurrentEnvironment()
为null,并且当调用它的getAttribute()
方法时,将抛出NPE。 我通过在单元测试代码中添加一些新的打印语句来证实这一点:
if(ApiProxy.getCurrentEnvironment() == null)
System.out.println("Environment is null.");
我隐约知道GAE为其所有服务提供“测试版本”,但未能(具体地)找到如何使用它们并进行设置。 所以我问:GAE是否提供这样的测试版本? 如果是这样,我如何在这里添加ApiProxy
测试版? 如果没有,那么我的选择是什么? 我不认为我可以模拟任何一种方法( ThreadManager#currentRequestThreadFactory
或ApiProxy#getCurrentEnvironment
),因为它们都是静态的。 提前致谢。
编辑 :我看到SDK附带了一个appengine-testing.jar
。 在这个JAR里面是一个ApiProxyLocal.class
,我相信它是一个可以在JUnit测试期间使用的ApiProxy
版本,它可以在不抛弃NPE的情况下工作。 如果是这种情况(我甚至不确定),那么问题是:如何将它注入我的ThreadManager
进行此测试?
如果您按照以下行设置LocalServiceTestHelper,您将从存根中获取正确的线程。
private static final LocalServiceTestHelper helper = new LocalServiceTestHelper( new LocalDatastoreServiceTestConfig());
@BeforeClass
public static void initialSetup() {
helper.setUp();
}
@AfterClass
public static void finalTearDown() {
helper.tearDown();
}
我建议不要直接从你的代码调用ThreadManager.currentRequestThreadFactory()
。 相反,将ThreadFactory
注入需要创建线程的类中。
一个简单的方法是使用Guice:
public static class MyService {
private final ThreadFactory threadFactory;
@Inject
MyService(ThreadFactory threadFactory) {
this.threadFactory = threadFactory;
}
...
}
在MyService
的测试中,您可以将假的ThreadFactory
传递给MyService
构造函数,也可以注入Executors.defaultThreadFactory()
。
在您的生产代码中,您将创建一个绑定:
bind(ThreadFactory.class)
.toInstance(ThreadManager.currentRequestThreadFactory());
当然,如果你还没准备好进入依赖注入,你可以创建自己的访问器来获取和设置ThreadFactory
如果您按照http://code.google.com/appengine/docs/java/howto/unittesting.html中的建议实施ApiProxy.Environment
然后getAttributes()
方法是您在地图中找不到REQUEST_THREAD_FACTORY_ATTR
的条目的地方。
你可以添加:
attributes.put(REQUEST_THREAD_FACTORY_ATTR, new RequestThreadFactory());
有关其他有用的补充信息,请参阅: http : //googleappengine.googlecode.com/svn-history/trunk/java/src/main/com/google/appengine/tools/development/LocalEnvironment.java
要添加到最后一个答案,如果要在单元测试用例中启动一个线程,那么该java线程也需要ApiProxy环境集。
在您的类LocalServiceTestCase扩展TestCase,安装方法可能如下所示:
super.setUp();
helper1.setUp();
setEnvironment();
哪里:
public static void setEnvironment() {
if (ApiProxy.getCurrentEnvironment() == null) {
ApiProxyLocal apl = LocalServiceTestHelper.getApiProxyLocal();
ApiProxy.setEnvironmentForCurrentThread(new TEnvironment());
ApiProxy.setDelegate(apl);
}
}
环境在上面的网址中给出。
您可能也想创建一个静态unsetEnvironment()。
在你的单元测试用例中,如果你已经启动了一个新的java线程,你可以在run方法的开头使用static方法:
public void run() {
LocalServiceTestCase.setEnvironment();
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.