EJB 3.1的单元测试

7
我正在对EJB 3.1的单元测试进行小型研究,最终目标是为单元测试EJB 3.1提供易于使用的解决方案。

  1. 我对大型EJB实现没有太多了解,因此我想首先请有经验的专家(您)就单元测试EJB中的难点提出意见。
  2. 通过我已经做过的初步研究,我可以理解使用Mocking框架进行单元测试的优点,而不是使用嵌入式容器。虽然两者都很好,但是在单元测试方面,Mocking框架略高于嵌入式容器。嵌入式容器当然非常好,并且具有自己的优点,但可能是单元测试的不同阶段。我仍然相信,在使用这种框架时,至少在某些情况下应该存在一些缺陷,这些缺陷可以得到改进。

我希望我能够为单元测试EJB制作完整的解决方案,一旦完成,我将在这个论坛上分享。

感谢您的支持。

3个回答

14

我向你提出的建议是不要陷入常见的陷阱,即认为你需要在模拟和使用嵌入式EJB容器之间进行选择。

你可以同时使用它们,应该同时使用它们,并且在你发现难以同时使用它们时,你应该要求EJB容器提供更好的支持和更多的功能。

当然,你会发现OpenEJB的人非常支持并乐于添加功能来支持获得最佳效果。几乎所有真正好的功能都是基于用户试图执行非常特定的任务并发现很难而创建的。

标准的EJBContainer API

package org.superbiz.stateless.basic;

import junit.framework.TestCase;

import javax.ejb.embeddable.EJBContainer;

public class CalculatorTest extends TestCase {

    private CalculatorBean calculator;

    /**
     * Bootstrap the Embedded EJB Container
     *
     * @throws Exception
     */
    protected void setUp() throws Exception {

        EJBContainer ejbContainer = EJBContainer.createEJBContainer();

        Object object = ejbContainer.getContext().lookup("java:global/simple-stateless/CalculatorBean");

        assertTrue(object instanceof CalculatorBean);

        calculator = (CalculatorBean) object;
    }

完整的源代码在这里

这会扫描类路径并加载所有bean。

无需扫描,更易于模拟的方法

稍微不同的方法是您在代码中定义所有内容。显然,模拟更容易,因为您可以随时提供所需的bean的模拟实现。

@RunWith(ApplicationComposer.class)
public class MoviesTest extends TestCase {

    @EJB
    private Movies movies;

    @Resource
    private UserTransaction userTransaction;

    @PersistenceContext
    private EntityManager entityManager;

    @Module
    public PersistenceUnit persistence() {
        PersistenceUnit unit = new PersistenceUnit("movie-unit");
        unit.setJtaDataSource("movieDatabase");
        unit.setNonJtaDataSource("movieDatabaseUnmanaged");
        unit.getClazz().add(Movie.class.getName());
        unit.setProperty("openjpa.jdbc.SynchronizeMappings", "buildSchema(ForeignKeys=true)");
        return unit;
    }

    @Module
    public EjbJar beans() {
        EjbJar ejbJar = new EjbJar("movie-beans");
        ejbJar.addEnterpriseBean(new StatefulBean(MoviesImpl.class));
        return ejbJar;
    }

    @Configuration
    public Properties config() throws Exception {
        Properties p = new Properties();
        p.put("movieDatabase", "new://Resource?type=DataSource");
        p.put("movieDatabase.JdbcDriver", "org.hsqldb.jdbcDriver");
        p.put("movieDatabase.JdbcUrl", "jdbc:hsqldb:mem:moviedb");
        return p;
    }

    @Test
    public void test() throws Exception {

        userTransaction.begin();

        try {
            entityManager.persist(new Movie("Quentin Tarantino", "Reservoir Dogs", 1992));
            entityManager.persist(new Movie("Joel Coen", "Fargo", 1996));
            entityManager.persist(new Movie("Joel Coen", "The Big Lebowski", 1998));

            List<Movie> list = movies.getMovies();
            assertEquals("List.size()", 3, list.size());

            for (Movie movie : list) {
                movies.deleteMovie(movie);
            }

            assertEquals("Movies.getMovies()", 0, movies.getMovies().size());

        } finally {
            userTransaction.commit();
        }
    }
}

完整源代码

最终结果

很容易集中关注不同类型的测试之间的差异,但显然有些事情可以说是务实的。个人而言,我认为能够尽可能流畅地混合“单元”和“集成”样式没有任何问题。

当然,这是一个值得称赞的目标。为了让我们更接近,欢迎提供想法和功能请求。


你好,David。非常感谢你的回复。我也在考虑混合两种方法,这将有助于收获两种方法的好处。 - Bala

5

实际上,您可能需要考虑两种不同类型的测试(并非互斥):

  • 单元测试:您的EJB最终只是POJO,因此可以使用您喜欢的单元测试框架(例如JUnit),还可以使用Mockito或EasyMock等模拟框架。
  • 集成测试:在这里,您希望像在容器中一样测试EJB(而不是在隔离环境中),因此您必须以某种方式模拟该容器。您仍然可以使用单元测试框架编写测试代码(例如JUnit),但现在您正在测试这些EJB在容器中如何表现并与其他协作者(例如其他EJB)进行交互。对于这个问题,我建议使用Arquillian

感谢您的输入。我主要关注的是通过提供使开发人员更加优雅和简单的功能,使单元测试变得更简单。但是,正如您所提到的,更好的方法是使用嵌入式容器将UT和集成测试结合起来作为2个UT阶段,这似乎是一个不错的方法。 - Bala

3
你可以使用Needle来对Java EE组件进行单元测试。
Needle是一个轻量级的框架,用于在容器之外隔离地测试Java EE组件。它通过分析依赖关系和自动注入模拟对象来减少测试设置代码。

http://needle.spree.de


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接