且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

使用 JUnit 测试 EJB

更新时间:2023-12-04 22:03:52

首先,确保区分单元测试集成测试.JUnit 只是一个帮助您组织和运行测试的框架,但您必须确定测试的范围.

First of all, make sure you distinguish between unit tests and integration tests. JUnit is just a framework that helps you organize and run the tests, but you have to determine the scope of your tests.

我假设您有兴趣定义 CommentService.findAll()单元测试.这意味着什么?这意味着我将验证调用 findAll() 方法会导致 CommentService 调用由 FIND_ALL 字符串常量命名的命名查询.

I assume you're interested in defining a unit test of CommentService.findAll(). What does that mean? That means I'll verify that calling the findAll() method results in CommentService invoking the named query named by the FIND_ALL string constant.

多亏了依赖注入和存根,你可以很容易地使用例如Mockito 来删除 EntityManager.对于单元测试,我们只关注findAll()中的业务逻辑,所以我也不会去测试Comment服务的查找——测试Comment服务是否可以查找并且连接到适当的实体管理器实例属于集成测试的范围,而不是单元测试.

Thanks to dependency injection and stubbing, you can easily achieve that using e.g. Mockito to stub out the EntityManager. For the unit test, we're only focusing on the business logic in findAll(), so I won't bother testing lookup of the Comment service either--testing that the Comment service can be looked up and is wired to a proper entity manager instance is in the scope of an integration test, not a unit test.

public class MyCommentServiceUnitTest {
    CommentService commentService;
    EntityManager entityManager;

    @Before
    public void setUp() {
        commentService = new CommentService();

        entityManager = mock(EntityManager.class);
        commentService.setEm(entityManager); // inject our stubbed entity manager
    }

    @Test
    public void testFindAll() {
        // stub the entity manager to return a meaningful result when somebody asks
        // for the FIND_ALL named query
        Query query = mock(Query.class);
        when(entityManager.createNamedQuery(Comment.FIND_ALL, Comment.class)).thenReturn(query);
        // stub the query returned above to return a meaningful result when somebody
        // asks for the result list
        List<Comment> dummyResult = new LinkedList<Comment>();
        when(query.getResultList()).thenReturn(dummyResult);

        // let's call findAll() and see what it does
        List<Comment> result = commentService.findAll();

        // did it request the named query?
        verify(entityManager).createNamedQuery(Comment.FIND_ALL, Comment.class);
        // did it ask for the result list of the named query?
        verify(query).getResultList();
        // did it return the result list of the named query?
        assertSame(dummyResult, result);

        // success, it did all of the above!
    }
}

通过上面的单元测试,我测试了 findAll() 实现的行为.单元测试验证获得了正确的命名查询,并将命名查询返回的结果返回给了被调用者.

With the unit test above, I tested the behavior of the findAll() implementation. The unit test verified that the correct named query is obtained and that the result returned by the named query was returned to the callee.

此外,上面的单元测试独立于底层 JPA 提供程序和底层数据验证 findAll() 的实现是否正确.我不想测试 JPA 和 JPA 提供程序,除非我怀疑第 3 方代码中存在错误,因此剔除这些依赖项让我可以将测试完全集中在评论服务的业务逻辑上.

What's more, the unit test above verifies that the implementation of findAll() is correct independently of the underlying JPA provider and the underlying data. I don't want to test JPA and the JPA provider unless I suspect there are bugs in the 3rd party code, so stubbing out these dependencies lets me focus the test entirely on the business logic of the Comment service.

适应使用存根测试行为的心态可能需要一些时间,但它是一种非常强大的技术,用于测试 EJB 3.1 bean 的业务逻辑,因为它允许您隔离每个测试的范围并将其缩小到排除外部依赖.

It can take a little while to adjust to the mindset of testing behavior using stubs, but it is a very powerful technique for testing the business logic of your EJB 3.1 beans because it lets you isolate and narrow the scope of each test to exclude external dependencies.