Mat*_*ias 39 junit jpa java-ee java-ee-6
我应该如何测试获取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();
}
}
Run Code Online (Sandbox Code Playgroud)
可能的测试:
@Test
public void testFindAll() {
List<Comment> all = service.findAll();
Assert.assertEquals(8, all.size());
}
Run Code Online (Sandbox Code Playgroud)
我只使用GlassFish 3.1和Eclipse Indigo for Java EE Developers.我已经尝试过这样的事情:
@Before
public void setUp() throws Exception {
ejbContainer = EJBContainer.createEJBContainer();
service = (CommentService) ejbContainer.getContext()
.lookup("java:global/classes/CommentService");
}
Run Code Online (Sandbox Code Playgroud)
但我得到的只是:
javax.ejb.EJBException:
No EJBContainer provider available: no provider names had been found.
Run Code Online (Sandbox Code Playgroud)
Oli*_*ins 78
接受的答案需要模拟很多代码,包括持久层.使用嵌入式容器来测试实际的bean; 否则,模拟持久层会导致代码几乎无法测试任何有用的东西.
使用具有引用持久性单元的实体管理器的会话bean:
@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;
}
}
Run Code Online (Sandbox Code Playgroud)
实体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;
}
}
Run Code Online (Sandbox Code Playgroud)
此持久性单元在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>
Run Code Online (Sandbox Code Playgroud)
交易类型必须是JTA
.
然后编写一个创建和销毁EJB容器的测试(GlassFish嵌入式容器):
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());
}
}
Run Code Online (Sandbox Code Playgroud)
接下来,添加两个依赖项(例如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>
Run Code Online (Sandbox Code Playgroud)
具有依赖性,会话和实体 bean的,持久性的文件,测试执行文件完全一样所示,那么测试(S)应该通过.(互联网上的例子非常不合适.)
Kim*_*ard 43
首先,确保区分单元测试和集成测试.JUnit只是一个框架,可以帮助您组织和运行测试,但您必须确定测试的范围.
我假设你有兴趣定义一个单元测试CommentService.findAll()
.那是什么意思?这意味着我将验证调用该findAll()
方法会导致CommentService调用由FIND_ALL
字符串常量命名的命名查询.
由于依赖注入和存根,您可以轻松地使用例如Mockito来实现这一点EntityManager
.对于单元测试,我们只关注业务逻辑findAll()
,所以我也不会费心测试Comment服务的查找 - 测试Comment服务可以查找并连接到正确的实体管理器实例是在集成测试的范围内,而不是单元测试.
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!
}
}
Run Code Online (Sandbox Code Playgroud)
通过上面的单元测试,我测试了实现的行为findAll()
.单元测试验证是否获得了正确的命名查询,并且命名查询返回的结果被返回给被调用者.
更重要的是,上面的单元测试验证了findAll()
独立于底层JPA提供程序和底层数据的实现是否正确.我不想测试JPA和JPA提供程序,除非我怀疑第三方代码中存在错误,因此删除这些依赖关系让我将测试完全集中在Comment服务的业务逻辑上.
使用存根调整测试行为的思维方式可能需要一段时间,但它是一种非常强大的技术,用于测试EJB 3.1 bean的业务逻辑,因为它可以让您隔离和缩小每个测试的范围以排除外部依赖性.
为什么不使用Arquillian编写甚至单元测试并在真正的容器中运行它们?
没有更多的嘲笑.不再有容器生命周期和部署麻烦.只是真正的测试!
模拟可以是战术性的,但通常用于使代码在真实环境之外工作.Arquillian让你放弃嘲笑并编写真正的测试.这是因为Arquillian将您的测试带到运行时,使您可以访问容器资源,有意义的反馈以及有关代码如何工作的洞察力.
有关Arquillian功能的更多信息.
归档时间: |
|
查看次数: |
64745 次 |
最近记录: |