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

后端 未结 5 746
旧巷少年郎
旧巷少年郎 2021-01-17 16:55

I\'ve got some Objectify test code running in JUnit and I\'m getting this error:

java.lang.IllegalStateException: You have not started an Objectify context.          


        
相关标签:
5条回答
  • 2021-01-17 17:29

    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();
        }
    }
    
    0 讨论(0)
  • 2021-01-17 17:53

    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.

    0 讨论(0)
  • 2021-01-17 17:53

    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>
    
    0 讨论(0)
  • 2021-01-17 17:54

    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();
        }
    
    0 讨论(0)
  • 2021-01-17 17:56

    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.
    
    0 讨论(0)
提交回复
热议问题