How to resolve “You have not started an Objectify context” in JUnit?

99封情书 提交于 2019-12-01 16:26:32

Jeff Schnitzer answered this here: https://groups.google.com/forum/#!topic/objectify-appengine/8HinahG7irg. That link points to https://groups.google.com/forum/#!topic/objectify-appengine/O4FHC_i7EGk where Jeff suggests the following quick and dirty workaround:

  • My @BeforeMethod starts an objectify context (ObjectifyService.begin())

  • My @AfterMethod closes the objectify context

Jeff suggests we use ObjectifyService.run() instead but admits it's more work.

Here's how my implementation looks:

public class DownloadTaskRepositoryImplTest {
    // maximum eventual consistency (see https://cloud.google.com/appengine/docs/java/tools/localunittesting)
    private final LocalServiceTestHelper helper =
        new LocalServiceTestHelper(new LocalDatastoreServiceTestConfig()
            .setDefaultHighRepJobPolicyUnappliedJobPercentage(100));

    private Closeable closeable;

    @Before
    public void setUp() {
        helper.setUp();
        ObjectifyRegistrar.registerDataModel();
        closeable = ObjectifyService.begin();
    }

    @After
    public void tearDown() {
        closeable.close();

        helper.tearDown();
    }

I also had this issue and noticed that I had not added the ObjectifyFilter to my web.xml

<filter>
    <filter-name>ObjectifyFilter</filter-name>
    <filter-class>com.googlecode.objectify.ObjectifyFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>ObjectifyFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

I also had to include Objectify and guava jars in my WEB-INF>lib directory and include them in my build path.

I was facing the same error and this solusion worked for me

I have an app based on Endpoints that uses Objectify. When I leave it with the default/automatic scaling, everything works great. Once I enable basic scaling, though, I get the following exception when executing the endpoint method:

[INFO] java.lang.IllegalStateException: You have not started an Objectify context. You are probably missing the ObjectifyFilter. If you are not running in the context of an http request, see the ObjectifyService.run() method.
[INFO]  at com.googlecode.objectify.ObjectifyService.ofy(ObjectifyService.java:44)
[INFO]  at com.myco.myapp.dao.datastore.OfyService.ofy(OfyService.java:62)

The good news is that this goes away when you enable RequestDispatcher support in the web.xml file like so. I think this is a documentation issue, then, but I didn't know if everyone would agree if I edited the Wiki page directly. Here is the proposed web.xml entry, which worked for me:

   <filter>
    <filter-name>ObjectifyFilter</filter-name>
    <filter-class>com.googlecode.objectify.ObjectifyFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>ObjectifyFilter</filter-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>INCLUDE</dispatcher>
    <dispatcher>FORWARD</dispatcher>
</filter-mapping>

Improving michael-osofsky answer, I add this to my ofy helper class

public static void registerDataModel() {
    try {
        factory().register(Profile.class);
    } catch (Exception e){
        e.printStackTrace();
    }
}

and remplace

ObjectifyRegistrar.registerDataModel();

for this

OfyService.registerDataModel();

OfyService.java

public static void registerDataModel() {
    try {
        factory().register(Profile.class);
    } catch (Exception e){
        e.printStackTrace();
    }
}

As Jeff Schnitzer says in the link provided by Michael Osofsky:

In your tests you should have some notion of a 'request' even if it is just conceptual. If "each test is a request by itself", then you can use @Before/@After in conjunction with ObjectifyService.begin() to demarcate the requests. However, this is probably not actually how your tests work - it isn't how my tests work.

He then goes on to say:

This would be prettier with JDK8 closures but the idea is straightforward - you're wrapping some unit of work in a context which represents a request. It would probably be smart to add even more context like authentication in that wrapper too.

I came up with the following implementation of his idea. With the solution below, you can ensure each call to a servlet handler gets a fresh Objectify session while still making your servlet handler calls in a single line of code. It also decouples your tests from explicitly worrying about Objectify, and allows you to add additional non-Objectify context around your servlet handlers.

My solution below works with Objectify 5.1.22. I tried using Objectify 6+, but I had problems that seem to be related to this.

First, define a custom Supplier that is able to capture the exceptions thrown by a servlet handler.

  @FunctionalInterface
  public interface ServletSupplier<T> {

  T get()
    throws ServletException, IOException;
  }

Next, define a wrapper method that accepts your new custom Supplier as an input, and wrap the call to ServletSupplier.get() in a try-with-resources block that calls ObjectifyService.begin(). You must also register your entity classes before calling ServletSupplier.get().

  public <T> T runInServletContext(ServletSupplier<T> servletMethod)
      throws ServletException, IOException {

    try (Closeable session = ObjectifyService.begin()) {
      ObjectifyService.register(MyObj.class);
      return servletMethod.get();
    }
  }

Finally, anywhere in your tests that you call the servlet handler you should do so using the wrapper method.

  MyObj myObjPost = runInServletContext(() -> getServlet().doPost(request, response));
  // Assert results of doPost call.
  MyObj myObjGet = runInServletContext(() -> getServlet().doGet(request, response));
  // Assert results of doGet call.
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!