Reputation:
I've written a JUnit (4.10) unit test that makes the following call to com.google.appengine.api.ThreadManager
:
ThreadManager.currentRequestThreadFactory();
When this test runs, I get a NullPointerException
being thrown from within this currentRequestThreadFactory
method:
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)
When I pull down the source for ThreadManager
, and look at Line 39 (which is the source of the NPE), I see:
public static ThreadFactory currentRequestThreadFactory() {
return (ThreadFactory) ApiProxy.getCurrentEnvironment().getAttributes()
.get(REQUEST_THREAD_FACTORY_ATTR);
}
So it seems that the ApiProxy.getCurrentEnvironment()
is null, and when it's getAttribute()
method is called, the NPE is thrown. I've confirmed this by adding some new print statements higher up in my unit test code:
if(ApiProxy.getCurrentEnvironment() == null)
System.out.println("Environment is null.");
I'm vaguely aware that GAE offers "test versions" for all its services, but haven't been able to find (specifically) how to use them and set them up. So I ask: does GAE offer such test versions? If so, how do I add an ApiProxy
test version here? And if not, then what are my options? I don't think I can mock either method (ThreadManager#currentRequestThreadFactory
or ApiProxy#getCurrentEnvironment
) because they're both statics. Thanks in advance.
Edit: I see that there is an appengine-testing.jar
that ships with the SDK. Inside this JAR is an ApiProxyLocal.class
that I believe is a version of ApiProxy
that could be used during JUnit testing, that would work without throwing the NPE. If that's the case (which I'm not even sure of), then the question is: how do I inject it into my ThreadManager
for this test?
Upvotes: 7
Views: 1504
Reputation: 81
You will get the correct Threads from the stubs, if you set up your LocalServiceTestHelper, along the following lines.
private static final LocalServiceTestHelper helper = new LocalServiceTestHelper( new LocalDatastoreServiceTestConfig());
@BeforeClass
public static void initialSetup() {
helper.setUp();
}
@AfterClass
public static void finalTearDown() {
helper.tearDown();
}
Upvotes: 4
Reputation: 1
To add to the last answer, if you are starting a thread within your unit test case, that java thread needs the ApiProxy environment set too.
in your class LocalServiceTestCase extends TestCase, the setup method might look something like this:
super.setUp();
helper1.setUp();
setEnvironment();
where:
public static void setEnvironment() {
if (ApiProxy.getCurrentEnvironment() == null) {
ApiProxyLocal apl = LocalServiceTestHelper.getApiProxyLocal();
ApiProxy.setEnvironmentForCurrentThread(new TEnvironment());
ApiProxy.setDelegate(apl);
}
}
The TEnvironment is given in the above urls.
You might want to make a static unsetEnvironment() too.
In your unit test case, if you've started a new java thread, you can just use the static method at the beginning of your run method:
public void run() {
LocalServiceTestCase.setEnvironment();
Upvotes: 0
Reputation: 1
If you're implementing ApiProxy.Environment
as suggested in
http://code.google.com/appengine/docs/java/howto/unittesting.html
then the getAttributes()
method is where you are not finding an
entry for REQUEST_THREAD_FACTORY_ATTR
in your map.
You can add that:
attributes.put(REQUEST_THREAD_FACTORY_ATTR, new RequestThreadFactory());
For other helpful additions, see: http://googleappengine.googlecode.com/svn-history/trunk/java/src/main/com/google/appengine/tools/development/LocalEnvironment.java
Upvotes: 0
Reputation: 24286
I suggest avoiding calling ThreadManager.currentRequestThreadFactory()
directly from your code. Instead, inject the ThreadFactory
into the class that needs to create threads.
One easy way to do this is with Guice:
public static class MyService {
private final ThreadFactory threadFactory;
@Inject
MyService(ThreadFactory threadFactory) {
this.threadFactory = threadFactory;
}
...
}
In your tests for MyService
, you could either pass a fake ThreadFactory
to the MyService
constructor or inject Executors.defaultThreadFactory()
.
In your production code, you would create a binding:
bind(ThreadFactory.class)
.toInstance(ThreadManager.currentRequestThreadFactory());
Of course, if you're not ready to jump into dependency injection, you can create your own accessors to get and set the ThreadFactory
Upvotes: 1