EJB3:如何在Junit测试期间将EJB3中的DataSource注入原始POJO

时间:2012-11-08 17:55:51

标签: dependency-injection junit ejb-3.0

关于在使用junit通过其原始POJO API对(EJB 3.0)EJB进行集成测试期间注入备用数据源的快速问题。

我一直在将原始POJO服务转换为EJB3会话bean。这样做实际上只是意味着直接注释POJO。这些服务还附带现有的junit集成测试(检查查询真实测试数据库的方法的结果)。

其中一些服务需要直接的java.sql.Connection,所以我打算通过注入的DataSource来配置它。这样做的目的是我可以将bean直接部署到app服务器(WLS,因为它发生)。但是,我还希望现有的集成测试能够继续工作。这些测试针对自己的测试数据库运行,因此我需要能够在运行集成测试时注入测试配置(在POJO /非容器环境中)。

问题是:

  

一旦我设置了EJB,如果不在容器内运行,我是否无法覆盖注入的bean?

     

换句话说,在运行原始POJO集成测试时,是否没有直接的方法来注入新的JNDI配置?

示例服务类似于以下内容:

@Stateless(mappedName="MyInterface")
public class MyClassImpl implements MyInterface {
    ... 
    @Resource(name="jdbc/MyAppServerDataSourceJNDIName")
    DataSource ds;
    Connection conn;
...
}

N.B。我不打算将DataSource和Connection留在服务中,我只想在有机重构之前得到一些合理的工作。

我正在考虑的解决方案:

  1. 我有一个(非常糟糕的)概念就是在服务中提供一个set-private的setter。这样,我的junit测试可以在执行之前设置Connection。然后,在app-server环境中,将使用注入的DS。虽然不漂亮。
  2. 我看了一下ejb3unit(BaseSessionBeanFixture)并考虑到了这一点。
  3. 我也明白我可以在junit中创建一个EJB容器并在容器中运行。问题是,我想使用简单的junit测试和针对POJO(而不是EJB)来测试基本功能。
  4. 我知道这可以在春天完成(我有点像EJB新手)并且正在考虑使用spring config连接EJB。
  5. 有很多信息,但没有具体的信息(主要是JPA)。虽然在SO的其他地方有一些很好的指示。

    提前致谢。

2 个答案:

答案 0 :(得分:0)

我的建议:

  1. 删除conn字段,并始终使用ds.getConnection()(和conn.close())。在单元测试环境中,您可以模拟DataSource / Connection(或以其他方式提供获取并提供连接到测试数据库的“真实”对象)。
  2. 将一个setter添加到EJB类。实际上,您可以注释注入方法,以便您的容器和单元测试环境更加接近。你说添加一个setter会“非常糟糕”,但我不确定为什么;安装者非常注重依赖注入的精神。我同意将setter添加到业务接口是没有意义的,但是将它添加到bean类似乎很好。
  3. #2的例子:

    @Resource(name="jdbc/MyAppServerDataSourceJNDIName")
    public void setDataSource(DataSource ds) {
        this.ds = ds;
    }
    

答案 1 :(得分:0)

这是附加样板的框架,用于为您的服务提供可注入的连接(仅限package),以便EJB可用作app-container中的EJB,也可用作本地可配置的junit测试的POJO连接。请注意,还有额外的样板,我们依靠调用#closeConnection来正确管理它。

这个解决方案可以回到最初的问题和bkail的回应:

@Stateless(mappedName = "MyClass")
...
public class MyClass {

  // DataSource as configured for app-server environment
  @Resource(name="app-dataSource")
  private DataSource appDataSource;

  private Connection conn;

  /**
   * Injectable {@code Connection} for injection by unit tests.
   */
  /*package-private*/ void setConnection(Connection conn) {
      this.conn = conn;
  }

  /**
   * Get {@code Connection}. Use injected Connection if supplied, otherwise
   * obtain one from the datasource. Client is responsible for closing this
   * through call to #closeConnection (only!).
   */
  final Connection getConnection() {
    if (this.conn != null) {
      return this.conn;
    } else {
      //Needs exception handling
      //
      return getDataSource().getConnection)
    }
  }

  /*package-private*/ final void closeConnection(Connection conn) {
    if (conn != null && conn != this.conn) {
      try{
        conn.close();
      } catch ... {}
    }
  }

  /*
   * Accessor for the {@code DataSource}.
   * @return the DataSource
   */
  private DataSource getDataSource() {
    if (ds == null) {
      ...
      ds = (DataSource) ctx.lookup(jndiName);
      ...
    }
    return ds;
  }

  ...
}

public class TestMyClass {
  private Connection conn = null;

  @BeforeClass
  public void setUpBeforeClass() throws Exception {
    ...
    conn = DriverManager.getConnection(...);
  }

  @Test
  public void testMyMethod {
    MyClass mc = new MyClass();
    mc.setConnection(conn);           // Set our own connection

    // do test stuff
    ...
  }
  ...
}