如何在 JUnit 中解决“您尚未启动 Objectify 上下文”?

Posted

技术标签:

【中文标题】如何在 JUnit 中解决“您尚未启动 Objectify 上下文”?【英文标题】:How to resolve "You have not started an Objectify context" in JUnit? 【发布时间】:2015-02-27 21:18:29 【问题描述】:

我在 JUnit 中运行了一些 Objectify 测试代码,但出现此错误:

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.
    at com.googlecode.objectify.ObjectifyService.ofy(ObjectifyService.java:44)
    at com.googlecode.objectify.impl.ref.LiveRef.<init>(LiveRef.java:31)
    at com.googlecode.objectify.Ref.create(Ref.java:26)
    at com.googlecode.objectify.Ref.create(Ref.java:32)
    at com.netbase.followerdownloader.repository.DownloadTaskRepositoryImpl.create(DownloadTaskRepositoryImpl.java:35)
    at com.netbase.followerdownloader.repository.DownloadTaskRepositoryImplTest.setUp(DownloadTaskRepositoryImplTest.java:45)

如何为测试代码解决这个问题?

【问题讨论】:

【参考方案1】:

作为 Michael Osofsky 提供的链接中的Jeff Schnitzer says:

在您的测试中,您应该有一些“请求”的概念,即使它只是概念性的。如果“每个测试本身就是一个请求”,那么您可以使用 @Before/@After 结合 ObjectifyService.begin() 来划分请求。但是,这实际上可能不是您的测试的工作方式 - 这不是我的测试的工作方式。

他接着说:

使用 JDK8 闭包会更漂亮,但想法很简单——您将一些工作单元包装在代表请求的上下文中。在该包装器中添加更多上下文(例如身份验证)可能会很聪明。

我想出了以下实现他的想法。使用下面的解决方案,您可以确保对 servlet 处理程序的每次调用都获得一个新的 Objectify 会话,同时仍然在一行代码中调用 servlet 处理程序。它还将您的测试与明确担心 Objectify 分离,并允许您在 servlet 处理程序周围添加额外的非 Objectify 上下文。

我的以下解决方案适用于 Objectify 5.1.22。我尝试使用 Objectify 6+,但我遇到了似乎与 this 相关的问题。

首先,定义一个自定义供应商,它能够捕获 servlet 处理程序抛出的异常。

  @FunctionalInterface
  public interface ServletSupplier<T> 

  T get()
    throws ServletException, IOException;
  

接下来,定义一个接受新的自定义供应商作为输入的包装方法,并将对 ServletSupplier.get() 的调用包装在调用 ObjectifyService.begin() 的 try-with-resources 块中。您还必须在调用 ServletSupplier.get() 之前注册您的实体类。

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

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

最后,在测试中调用 servlet 处理程序的任何地方,都应该使用 wrapper 方法。

  MyObj myObjPost = runInServletContext(() -> getServlet().doPost(request, response));
  // Assert results of doPost call.
  MyObj myObjGet = runInServletContext(() -> getServlet().doGet(request, response));
  // Assert results of doGet call.

【讨论】:

【参考方案2】:

改进 michael-osofsky 的答案,我将其添加到我的 ofy 助手类中

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

替换

ObjectifyRegistrar.registerDataModel();

为此

OfyService.registerDataModel();

OfyService.java

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

【讨论】:

【参考方案3】:

我遇到了同样的错误,this solusion 为我工作

我有一个基于 Endpoints 的应用程序,它使用 Objectify。当我将其保留为默认/自动缩放时,一切正常。但是,一旦启用基本缩放,在执行端点方法时会出现以下异常:

[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)

好消息是,当您启用 RequestDispatcher 时,这种情况就会消失 像这样在 web.xml 文件中提供支持。我认为这是一个文档 问题,然后,但我不知道如果我编辑了每个人是否会同意 维基页面直接。这是建议的 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>
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>INCLUDE</dispatcher>
    <dispatcher>FORWARD</dispatcher>
</filter-mapping>

【讨论】:

【参考方案4】:

我也遇到了这个问题,并注意到我没有将 ObjectifyFilter 添加到我的 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>

我还必须在我的 WEB-INF>lib 目录中包含 Objectify 和 guava jar,并将它们包含在我的构建路径中。

【讨论】:

这可能是我的问题。这在 web.xml 中的什么位置??【参考方案5】:

Jeff Schnitzer 在这里回答了这个问题:https://groups.google.com/forum/#!topic/objectify-appengine/8HinahG7irg。该链接指向https://groups.google.com/forum/#!topic/objectify-appengine/O4FHC_i7EGk,其中 Jeff 建议使用以下快速而肮脏的解决方法:

我的@BeforeMethod 启动一个对象化上下文 (ObjectifyService.begin())

我的@AfterMethod 关闭了对象化上下文

Jeff 建议我们改用 ObjectifyService.run(),但他承认这需要更多的工作。

我的实现如下所示:

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();
    

【讨论】:

@Michael...您从哪里获得“ObjectifyRegistrar”? 此解决方案无法正常工作,“ObjectifyRegistrar”不存在 ObjectifyRegistrar 大概意思是调用 ObjectifyService.register()。

以上是关于如何在 JUnit 中解决“您尚未启动 Objectify 上下文”?的主要内容,如果未能解决你的问题,请参考以下文章

如何解决“论点不同! Junit 和 Mockito 中的通缉犯错误

如何在junit中编写字符串数组输入的测试用例?(junit的新增内容)

如何解决JUnit4单元测试报错 :method initializationerror not found

如何在 JUnit XML 报告中配置 name 和 classname 属性

错误:(23、17)无法解决:junit:junit:4.12

在 Eclipse 的 JUnit 视图中排序单元测试