5

我编写了一个 JUnit (4.10) 单元测试,它对以下内容进行了调用com.google.appengine.api.ThreadManager

ThreadManager.currentRequestThreadFactory();

当这个测试运行时,我NullPointerException从这个currentRequestThreadFactory方法中被抛出:

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#currentRequestThreadFactoryApiProxy#getCurrentEnvironment),因为它们都是静态的。提前致谢。

编辑:我看到appengine-testing.jarSDK 附带了一个。在这个 JAR 里面是一个ApiProxyLocal.class相信可以在 JUnit 测试期间使用的版本,ApiProxy它可以在不抛出 NPE 的情况下工作。如果是这种情况(我什至不确定),那么问题是:我如何将它注入我ThreadManager的测试中?

4

4 回答 4

3

如果按照以下几行设置 LocalServiceTestHelper,您将从存根中获得正确的线程。

private static final LocalServiceTestHelper helper = new LocalServiceTestHelper( new    LocalDatastoreServiceTestConfig());

@BeforeClass
public static void initialSetup() {
    helper.setUp();
}

@AfterClass
public static void finalTearDown() {
    helper.tearDown();
}
于 2014-02-18T20:04:41.023 回答
1

ThreadManager.currentRequestThreadFactory()我建议避免直接从您的代码中调用。相反,将 注入ThreadFactory到需要创建线程的类中。

一种简单的方法是使用 Guice:

public static class MyService {
  private final ThreadFactory threadFactory;

  @Inject
  MyService(ThreadFactory threadFactory) {
    this.threadFactory = threadFactory;
  }

  ...
}

在您的测试中MyService,您可以将假传递ThreadFactoryMyService构造函数或注入Executors.defaultThreadFactory()

在您的生产代码中,您将创建一个绑定:

bind(ThreadFactory.class)
    .toInstance(ThreadManager.currentRequestThreadFactory());

当然,如果你还没有准备好进入依赖注入,你可以创建自己的访问器来获取和设置ThreadFactory

于 2013-02-05T04:43:52.560 回答
0

如果您按照http://code.google.com/appengine/docs/java/howto/unittesting.htmlApiProxy.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

于 2013-08-13T23:06:40.360 回答
0

要添加到最后一个答案,如果您在单元测试用例中启动一个线程,则该 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);

    }
}

上述网址中给出了 TEnvironment。

您可能也想制作一个静态的 unsetEnvironment() 。

在您的单元测试用例中,如果您启动了一个新的 java 线程,您可以在 run 方法的开头使用静态方法:

public void run() {

    LocalServiceTestCase.setEnvironment();
于 2013-08-28T20:34:19.937 回答