用JUnittesting一个EJB
我应该如何testing一个获得EntityManager实例注入的EJB 3.1?
一个可能的EJB:
@Stateless @LocalBean public class CommentService { @PersistenceContext private EntityManager em; public List<Comment> findAll() { TypedQuery<Comment> query = em.createNamedQuery( Comment.FIND_ALL, Comment.class ); return query.getResultList(); } }
一个可能的testing:
@Test public void testFindAll() { List<Comment> all = service.findAll(); Assert.assertEquals(8, all.size()); }
我只使用GlassFish 3.1和Eclipse Indigo for Java EE开发人员。 我已经尝试过这样的事情:
@Before public void setUp() throws Exception { ejbContainer = EJBContainer.createEJBContainer(); service = (CommentService) ejbContainer.getContext() .lookup("java:global/classes/CommentService"); }
但我得到的只是:
javax.ejb.EJBException: No EJBContainer provider available: no provider names had been found.
首先,确保你区分unit testing和集成testing 。 JUnit只是一个帮助你组织和运行testing的框架,但你必须确定testing的范围。
我假设你有兴趣定义一个CommentService.findAll()
的单元testing。 那是什么意思? 这意味着我将validation调用findAll()
方法导致CommentService调用由FIND_ALL
string常量命名的命名查询。
感谢dependency injection和存根,你可以很容易地实现使用例如Mockito来存储EntityManager
。 对于unit testing,我们只关注findAll()
的业务逻辑,所以我不打扰testingComment服务的查询 – testingComment服务可以被查找并连接到适当的实体pipe理器实例处于集成testing的范围内,而不是unit testing。
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! } }
通过上面的unit testing,我testing了findAll()
实现的行为 。 unit testingvalidation是否获得了正确的命名查询,并将命名查询返回的结果返回给被调用者。
更重要的是,上面的unit testingvalidationfindAll()
的实现是正确的, findAll()
依赖于底层的JPA提供者和底层的数据。 我不想testingJPA和JPA提供程序,除非我怀疑第三方代码中有错误,所以将这些依赖关系排除在外可让我将testing完全集中在Comment服务的业务逻辑上。
使用存根调整testing行为的思路可能需要一点时间,但是它是testingEJB 3.1 bean的业务逻辑的一种非常强大的技术,因为它可以让您隔离和缩小每个testing的范围,以排除外部依赖关系。
我不同意这个问题的接受答案。 上面接受的答案涉及嘲笑包括持久层的许多代码。 你应该使用一个embedded的容器来testing实际的bean,否则嘲笑持久层会给你一个testing几乎没有用的testing。
原来的海报应该有一个这样的会话bean与一个实体pipe理器指向一个持久性单元:
@Stateless public class CommentService { @PersistenceContext(unitName = "pu") private EntityManager em; public void create(Comment t) { em.merge(t); } public Collection<Comment> getAll() { Query q = em.createNamedQuery("Comment.findAll"); Collection<Comment> entities = q.getResultList(); return entities; } }
实体bean看起来像这样:
@Entity @NamedQueries({@NamedQuery(name = "Comment.findAll", query = "select e from Comment e")}) public class Comment implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; public Long getId() { return id; } public void setId(Long id) { this.id = id; } }
这个持久性单元在persistence.xml文件中定义,如下所示:
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"> <persistence-unit name="pu" transaction-type="JTA"> <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider> <class>org.glassfish.embedded.tempconverter.Comment</class> <properties> <property name="eclipselink.ddl-generation" value="drop-and-create-tables"/> </properties> </persistence-unit> </persistence>
请注意,交易types需要是JTA。
你所要做的就是编写一个像这样的testing,创build和销毁EJB容器(glassfish embedded container):
public class CommentTest extends TestCase { private Context ctx; private EJBContainer ejbContainer; @BeforeClass public void setUp() { ejbContainer = EJBContainer.createEJBContainer(); System.out.println("Opening the container" ); ctx = ejbContainer.getContext(); } @AfterClass public void tearDown() { ejbContainer.close(); System.out.println("Closing the container" ); } public void testApp() throws NamingException { CommentService converter = (CommentService) ctx.lookup("java:global/classes/CommentService"); assertNotNull(converter); Comment t = new Comment(); converter.create(t); t = new Comment(); converter.create(t); t = new Comment(); converter.create(t); t = new Comment(); converter.create(t); Collection<Comment> ts = converter.getAll(); assertEquals(4, ts.size()); } }
你只需要将这两个添加到你的依赖。 下面是我的依赖在我的Maven POM中的样子:
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.8.2</version> <scope>test</scope> <type>jar</type> </dependency> <dependency> <groupId>org.glassfish.main.extras</groupId> <artifactId>glassfish-embedded-all</artifactId> <version>3.1.2</version> <scope>compile</scope> </dependency>
如果你有你的依赖关系 , 会话和实体 bean, 持久性文件, testing文件完全像我这里实现那么你的testing应该通过。 我花了整整两天的时间才弄明白(互联网上的例子是可悲的)。
为什么不使用Arquillian甚至写unit testing,并在真正的容器中运行它们?
没有更多的嘲笑。 没有更多的容器生命周期和部署的麻烦。 只是真正的考验!
嘲笑可以是战术性的,但更多的时候,它们被用来使代码在真实环境之外工作。 Arquillian让你嘲笑嘲笑,写出真正的考验。 这是因为Arquillian将您的testing带入了运行时,让您能够访问容器资源,有意义的反馈和关于代码真正工作的洞察。
更多关于Arquillianfunction。
可以编写针对容器运行的unit testing,但要注意的是容器/应用程序服务器必须启动。 由于这不是很实际,一般的方法是使用“模拟”容器来运行你的unit testing。 为此,请查看JUnitEE或ejb3unit:
junitee
ejb3unit
- 如何在JPA中创build和处理复合主键
- Spring 3.0与Java EE 6.0
- “jta-datasource”和“资源本地”数据源之间的区别?
- 应用程序服务器和servlet容器之间的区别?
- 如何使用web.xml中的Servlet URL映射?
- 删除的对象将被级联重新保存(从关联中删除删除的对象)
- Java EE应用程序中的java.lang.NoSuchMethodError:org.apache.commons.codec.binary.Base64.encodeBase64String()
- 在Java世界中,包vs vs模块vs组件vs容器vs服务vs平台
- 我应该select==还是eq来比较EL中的string?