原文: https://howtodoinjava.com/best-practices/how-you-should-unit-test-dao-layer/

如果您正在基于 SpringHibernateJPA 构建的项目中 ,并且您想对其数据访问层(DAO)进行单元测试,那么本教程中提供的信息可能会对您有所帮助。 当我们要测试 DAO 层时,我们也需要访问数据库。 但是由于某些原因,可能会损坏测试数据(主要是为集成测试准备的),或者由于某些其他团队成员也需要访问该数据,所以可能由于某些原因而不允许您使用任何现有数据库。 为了解决此问题,我正在使用内存数据库 。 IM(内存中)数据库是个不错的选择,因为它不会留下任何回溯,并且您可以确保在每次测试之前都会得到空表(通常是一个好习惯)。

您应该如何对 DAO 层进行单元测试 - 图1

一个好的单元测试应该保持数据库状态与测试用例执行之前的状态相同。 它应该删除所有添加的数据; 并回滚所有更新。

  1. Table of Contents
  2. 1) Always create unit test specific configuration file
  3. 2) Writing unit tests for DAO layer
  4. 3) Package Structure

1)始终创建单元测试特定的配置文件

这可能是为 DAO 层创建单元测试的第一步。 理想情况下,测试应使用与应用相同的配置。 但是可能会有一些更改,这些更改仅针对单元测试。 要解决此问题,您应该创建另一个测试特定的配置文件,并添加/覆盖测试特定的配置更改

例如。 在主应用中,如果配置文件为application-context.xml,则应创建另一个文件application-context-test.xml,然后将原始配置导入到该文件的顶部。 然后覆盖您可能需要的 Bean 定义(例如,使用内存数据库而不是常规数据库)。

application-context-test.xml

  1. <beans xmlns="http://www.springframework.org/schema/beans"
  2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xmlns:context="http://www.springframework.org/schema/context"
  4. xmlns:tx="http://www.springframework.org/schema/tx"
  5. xmlns:mvc="http://www.springframework.org/schema/mvc"
  6. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
  7. http://www.springframework.org/schema/context/ http://www.springframework.org/schema/context/spring-context-3.0.xsd
  8. http://www.springframework.org/schema/tx/ http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
  9. http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd">
  10. <import resource="application-context.xml"/>
  11. <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
  12. <property name="driverClassName" value="org.hsqldb.jdbcDriver" />
  13. <property name="url" value="jdbc:hsqldb:mem:howtodoinjava" />
  14. <property name="username" value="sa" />
  15. <property name="password" value="" />
  16. </bean>
  17. </beans>

application-context.xml

  1. <beans xmlns="http://www.springframework.org/schema/beans"
  2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xmlns:context="http://www.springframework.org/schema/context"
  4. xmlns:tx="http://www.springframework.org/schema/tx"
  5. xmlns:mvc="http://www.springframework.org/schema/mvc"
  6. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
  7. http://www.springframework.org/schema/context/ http://www.springframework.org/schema/context/spring-context-3.0.xsd
  8. http://www.springframework.org/schema/tx/ http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
  9. http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd">
  10. <context:component-scan base-package="com.howtodoinjava.jpa.demo" />
  11. <bean id="entityManagerFactoryBean" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
  12. <property name="dataSource" ref="dataSource" />
  13. <property name="packagesToScan" value="com.howtodoinjava.jpa.demo.entity" />
  14. <property name="jpaVendorAdapter">
  15. <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
  16. </property>
  17. <property name="jpaProperties">
  18. <props>
  19. <prop key="hibernate.archive.autodetection">class,hbm</prop>
  20. <prop key="hibernate.hbm2ddl.auto">create</prop>
  21. <prop key="hibernate.show_sql">true</prop>
  22. <prop key="hibernate.dialect">org.hibernate.dialect.HSQLDialect</prop>
  23. </props>
  24. </property>
  25. </bean>
  26. <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
  27. <property name="driverClassName" value="com.mysql.jdbc.Driver" />
  28. <property name="url" value="jdbc:mysql://localhost:3306/test" />
  29. <property name="username" value="root" />
  30. <property name="password" value="password" />
  31. </bean>
  32. <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
  33. <property name="entityManagerFactory" ref="entityManagerFactoryBean" />
  34. </bean>
  35. <tx:annotation-driven />
  36. </beans>

在上面的示例中,我使用内存中数据源实现覆盖了常规数据源。

2)编写 DAO 层的单元测试

下一部分将编写 junit(或任何其他框架)测试用例。 我正在使用 Spring 测试模块。 您可以按照以下方式编写测试用例。

  1. package com.jpa.demo.test;
  2. import java.util.List;
  3. import org.junit.Assert;
  4. import org.junit.Test;
  5. import org.junit.runner.RunWith;
  6. import org.springframework.beans.factory.annotation.Autowired;
  7. import org.springframework.test.annotation.Rollback;
  8. import org.springframework.test.context.ContextConfiguration;
  9. import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
  10. import org.springframework.transaction.annotation.Transactional;
  11. import com.howtodoinjava.jpa.demo.dao.DepartmentDAO;
  12. import com.howtodoinjava.jpa.demo.dao.EmployeeDAO;
  13. import com.howtodoinjava.jpa.demo.entity.DepartmentEntity;
  14. import com.howtodoinjava.jpa.demo.entity.EmployeeEntity;
  15. @ContextConfiguration(locations = "classpath:application-context-test.xml")
  16. @RunWith(SpringJUnit4ClassRunner.class)
  17. public class TestEmployeeDAO
  18. {
  19. @Autowired
  20. private EmployeeDAO employeeDAO;
  21. @Autowired
  22. private DepartmentDAO departmentDAO;
  23. @Test
  24. @Transactional
  25. @Rollback(true)
  26. public void testAddDepartment()
  27. {
  28. DepartmentEntity department = new DepartmentEntity("Information Technology");
  29. departmentDAO.addDepartment(department);
  30. List<DepartmentEntity> departments = departmentDAO.getAllDepartments();
  31. Assert.assertEquals(department.getName(), departments.get(0).getName());
  32. }
  33. @Test
  34. @Transactional
  35. @Rollback(true)
  36. public void testAddEmployee()
  37. {
  38. DepartmentEntity department = new DepartmentEntity("Human Resource");
  39. departmentDAO.addDepartment(department);
  40. EmployeeEntity employee = new EmployeeEntity();
  41. employee.setFirstName("Lokesh");
  42. employee.setLastName("Gupta");
  43. employee.setEmail("howtodoinjava@gmail.com");
  44. employee.setDepartment(department);
  45. employeeDAO.addEmployee(employee);
  46. List<DepartmentEntity> departments = departmentDAO.getAllDepartments();
  47. List<EmployeeEntity> employees = employeeDAO.getAllEmployees();
  48. Assert.assertEquals(1, departments.size());
  49. Assert.assertEquals(1, employees.size());
  50. Assert.assertEquals(department.getName(), departments.get(0).getName());
  51. Assert.assertEquals(employee.getEmail(), employees.get(0).getEmail());
  52. }
  53. }

现在注意这里的几件事。

  1. 在启动测试套件之前,请加载测试配置文件。
    1. @ContextConfiguration(locations = "classpath:application-context-test.xml");
  1. 加载完主要配置后,您将可以轻松地将 DAO 引用直接注入到测试用例中。 ```java @Autowired private EmployeeDAO employeeDAO;

@Autowired private DepartmentDAO departmentDAO;

  1. 3.
  2. 使用`@Rollback(true)`注解可恢复原始数据库状态。
  3. ```java
  4. @Test
  5. @Transactional
  6. @Rollback(true)
  7. public void testAddDepartment()
  8. {
  9. //other code
  10. }
  1. 始终在测试用例中创建一些数据,并在同一测试用例中验证该数据。 切勿在另一个测试用例上使用一个测试用例。 您可能需要阅读以下文章中的一些单元测试最佳实践和准则。

    阅读更多:单元测试最佳实践

3)包结构

最后,看一下此示例中使用的项目结构。 注意,application-context-test.xml位于test/resources文件夹中。

您应该如何对 DAO 层进行单元测试 - 图2

包结构

现在查看我为本教程编写的其他文件。

log4j.properties

  1. log4j.logger.org.hibernate=INFO, hb
  2. log4j.logger.org.hibernate.type=TRACE
  3. log4j.appender.hb=org.apache.log4j.ConsoleAppender
  4. log4j.appender.hb.layout=org.apache.log4j.PatternLayout

DepartmentEntity.java

  1. package com.howtodoinjava.jpa.demo.entity;
  2. import java.io.Serializable;
  3. import java.util.ArrayList;
  4. import java.util.List;
  5. import javax.persistence.CascadeType;
  6. import javax.persistence.Entity;
  7. import javax.persistence.GeneratedValue;
  8. import javax.persistence.Id;
  9. import javax.persistence.OneToMany;
  10. import javax.persistence.Table;
  11. @Entity(name="DepartmentEntity")
  12. @Table (name="department")
  13. public class DepartmentEntity implements Serializable {
  14. private static final long serialVersionUID = 1L;
  15. @Id
  16. @GeneratedValue
  17. private Integer id;
  18. private String name;
  19. public DepartmentEntity(){
  20. }
  21. public DepartmentEntity(String name) {
  22. super();
  23. this.name = name;
  24. }
  25. @OneToMany(mappedBy="department",cascade=CascadeType.PERSIST)
  26. private List<EmployeeEntity> employees = new ArrayList<EmployeeEntity>();
  27. //Setters and Getters
  28. @Override
  29. public String toString() {
  30. return "DepartmentVO [id=" + id + ", name=" + name + "]";
  31. }
  32. }

EmployeeEntity.java

  1. package com.howtodoinjava.jpa.demo.entity;
  2. import java.io.Serializable;
  3. import javax.persistence.Entity;
  4. import javax.persistence.GeneratedValue;
  5. import javax.persistence.Id;
  6. import javax.persistence.ManyToOne;
  7. import javax.persistence.Table;
  8. @Entity(name="EmployeeEntity")
  9. @Table (name="employee")
  10. public class EmployeeEntity implements Serializable
  11. {
  12. private static final long serialVersionUID = 1L;
  13. @Id
  14. @GeneratedValue
  15. private Integer id;
  16. private String firstName;
  17. private String lastName;
  18. private String email;
  19. @ManyToOne
  20. private DepartmentEntity department;
  21. public EmployeeEntity() {}
  22. public EmployeeEntity(String name, DepartmentEntity department) {
  23. this.firstName = name;
  24. this.department = department;
  25. }
  26. public EmployeeEntity(String name) {
  27. this.firstName = name;
  28. }
  29. //Setters and Getters
  30. @Override
  31. public String toString() {
  32. return "EmployeeVO [id=" + id + ", firstName=" + firstName
  33. + ", lastName=" + lastName + ", email=" + email
  34. + ", department=" + department + "]";
  35. }
  36. }

DepartmentDAO.java

  1. package com.howtodoinjava.jpa.demo.dao;
  2. import java.util.List;
  3. import com.howtodoinjava.jpa.demo.entity.DepartmentEntity;
  4. public interface DepartmentDAO
  5. {
  6. public List<DepartmentEntity> getAllDepartments();
  7. public DepartmentEntity getDepartmentById(Integer id);
  8. public boolean addDepartment(DepartmentEntity dept);
  9. public boolean removeDepartment(DepartmentEntity dept);
  10. public boolean removeAllDepartments();
  11. }

DepartmentDAOImpl.java

  1. package com.howtodoinjava.jpa.demo.dao;
  2. import java.util.List;
  3. import javax.persistence.EntityManager;
  4. import javax.persistence.PersistenceContext;
  5. import javax.persistence.Query;
  6. import org.springframework.stereotype.Repository;
  7. import org.springframework.transaction.annotation.Transactional;
  8. import com.howtodoinjava.jpa.demo.entity.DepartmentEntity;
  9. @Repository
  10. @Transactional
  11. public class DepartmentDAOImpl implements DepartmentDAO {
  12. @PersistenceContext
  13. private EntityManager manager;
  14. @Override
  15. public List<DepartmentEntity> getAllDepartments() {
  16. List<DepartmentEntity> depts = manager.createQuery("Select a From DepartmentEntity a", DepartmentEntity.class).getResultList();
  17. return depts;
  18. }
  19. @Override
  20. public DepartmentEntity getDepartmentById(Integer id) {
  21. return manager.find(DepartmentEntity.class, id);
  22. }
  23. @Override
  24. public boolean addDepartment(DepartmentEntity dept) {
  25. try{
  26. manager.persist(dept);
  27. }catch(Exception e){
  28. e.printStackTrace();
  29. return false;
  30. }
  31. return true;
  32. }
  33. @Override
  34. public boolean removeDepartment(DepartmentEntity dept) {
  35. try{
  36. manager.remove(dept);
  37. }catch(Exception e){
  38. e.printStackTrace();
  39. return false;
  40. }
  41. return true;
  42. }
  43. @Override
  44. public boolean removeAllDepartments() {
  45. try{
  46. Query query = manager.createNativeQuery("DELETE FROM DEPARTMENT");
  47. query.executeUpdate();
  48. }catch(Exception e){
  49. e.printStackTrace();
  50. return false;
  51. }
  52. return true;
  53. }
  54. }

EmployeeDAO.java

  1. package com.howtodoinjava.jpa.demo.dao;
  2. import java.util.List;
  3. import com.howtodoinjava.jpa.demo.entity.EmployeeEntity;
  4. public interface EmployeeDAO
  5. {
  6. public List<EmployeeEntity> getAllEmployees();
  7. public List<EmployeeEntity> getAllEmployeesByDeptId(Integer id);
  8. public EmployeeEntity getEmployeeById(Integer id);
  9. public boolean addEmployee(EmployeeEntity employee);
  10. public boolean removeEmployee(EmployeeEntity employee);
  11. public boolean removeAllEmployees();
  12. }

EmployeeDAOImpl.java

  1. package com.howtodoinjava.jpa.demo.dao;
  2. import java.util.List;
  3. import javax.persistence.EntityManager;
  4. import javax.persistence.PersistenceContext;
  5. import javax.persistence.Query;
  6. import org.springframework.stereotype.Repository;
  7. import org.springframework.transaction.annotation.Transactional;
  8. import com.howtodoinjava.jpa.demo.entity.EmployeeEntity;
  9. @Repository
  10. @Transactional
  11. public class EmployeeDAOImpl implements EmployeeDAO {
  12. @PersistenceContext
  13. private EntityManager manager;
  14. @Override
  15. public List<EmployeeEntity> getAllEmployees() {
  16. List<EmployeeEntity> employees = manager.createQuery("Select a From EmployeeEntity a", EmployeeEntity.class).getResultList();
  17. return employees;
  18. }
  19. @Override
  20. public List<EmployeeEntity> getAllEmployeesByDeptId(Integer id) {
  21. List<EmployeeEntity> employees = manager.createQuery("Select a From EmployeeEntity a", EmployeeEntity.class).getResultList();
  22. return employees;
  23. }
  24. @Override
  25. public EmployeeEntity getEmployeeById(Integer id) {
  26. return manager.find(EmployeeEntity.class, id);
  27. }
  28. @Override
  29. public boolean addEmployee(EmployeeEntity employee) {
  30. try{
  31. manager.persist(employee);
  32. }catch(Exception e){
  33. e.printStackTrace();
  34. return false;
  35. }
  36. return true;
  37. }
  38. @Override
  39. public boolean removeEmployee(EmployeeEntity employee) {
  40. try{
  41. manager.remove(employee);
  42. }catch(Exception e){
  43. e.printStackTrace();
  44. return false;
  45. }
  46. return true;
  47. }
  48. @Override
  49. public boolean removeAllEmployees() {
  50. try{
  51. Query query = manager.createNativeQuery("DELETE FROM EMPLOYEE");
  52. query.executeUpdate();
  53. }catch(Exception e){
  54. e.printStackTrace();
  55. return false;
  56. }
  57. return true;
  58. }
  59. }

pom.xml

  1. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  2. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd;
  3. <modelVersion>4.0.0</modelVersion>
  4. <groupId>com.howtodoinjava.jpa.demo</groupId>
  5. <artifactId>JPAExamples</artifactId>
  6. <version>0.0.1-SNAPSHOT</version>
  7. <packaging>jar</packaging>
  8. <build>
  9. <sourceDirectory>src</sourceDirectory>
  10. <plugins>
  11. <plugin>
  12. <artifactId>maven-compiler-plugin</artifactId>
  13. <version>2.3.2</version>
  14. <configuration>
  15. <source>1.7</source>
  16. <target>1.7</target>
  17. </configuration>
  18. </plugin>
  19. </plugins>
  20. </build>
  21. <dependencies>
  22. <dependency>
  23. <groupId>junit</groupId>
  24. <artifactId>junit</artifactId>
  25. <version>4.12</version>
  26. <scope>test</scope>
  27. </dependency>
  28. <!-- Spring Support -->
  29. <dependency>
  30. <groupId>org.springframework</groupId>
  31. <artifactId>spring-core</artifactId>
  32. <version>4.1.4.RELEASE</version>
  33. </dependency>
  34. <dependency>
  35. <groupId>org.springframework</groupId>
  36. <artifactId>spring-context</artifactId>
  37. <version>4.1.4.RELEASE</version>
  38. </dependency>
  39. <dependency>
  40. <groupId>org.springframework</groupId>
  41. <artifactId>spring-orm</artifactId>
  42. <version>4.1.4.RELEASE</version>
  43. </dependency>
  44. <dependency>
  45. <groupId>org.springframework</groupId>
  46. <artifactId>spring-test</artifactId>
  47. <version>4.1.4.RELEASE</version>
  48. <scope>test</scope>
  49. </dependency>
  50. <dependency>
  51. <groupId>org.hibernate</groupId>
  52. <artifactId>hibernate-core</artifactId>
  53. <version>4.0.1.Final</version>
  54. </dependency>
  55. <dependency>
  56. <groupId>org.hibernate</groupId>
  57. <artifactId>hibernate-validator</artifactId>
  58. <version>4.2.0.Final</version>
  59. </dependency>
  60. <dependency>
  61. <groupId>org.hibernate.common</groupId>
  62. <artifactId>hibernate-commons-annotations</artifactId>
  63. <version>4.0.1.Final</version>
  64. </dependency>
  65. <dependency>
  66. <groupId>org.hibernate.javax.persistence</groupId>
  67. <artifactId>hibernate-jpa-2.0-api</artifactId>
  68. <version>1.0.1.Final</version>
  69. </dependency>
  70. <dependency>
  71. <groupId>org.hibernate</groupId>
  72. <artifactId>hibernate-entitymanager</artifactId>
  73. <version>4.0.1.Final</version>
  74. </dependency>
  75. <dependency>
  76. <groupId>javax.validation</groupId>
  77. <artifactId>validation-api</artifactId>
  78. <version>1.0.0.GA</version>
  79. </dependency>
  80. <dependency>
  81. <groupId>org.slf4j</groupId>
  82. <artifactId>slf4j-api</artifactId>
  83. <version>1.7.5</version>
  84. </dependency>
  85. <dependency>
  86. <groupId>org.slf4j</groupId>
  87. <artifactId>slf4j-log4j12</artifactId>
  88. <version>1.5.6</version>
  89. </dependency>
  90. <dependency>
  91. <groupId>org.jboss.logging</groupId>
  92. <artifactId>jboss-logging</artifactId>
  93. <version>3.1.0.CR2</version>
  94. </dependency>
  95. <dependency>
  96. <groupId>mysql</groupId>
  97. <artifactId>mysql-connector-java</artifactId>
  98. <version>5.1.10</version>
  99. </dependency>
  100. <dependency>
  101. <groupId>commons-dbcp</groupId>
  102. <artifactId>commons-dbcp</artifactId>
  103. <version>1.4</version>
  104. </dependency>
  105. <dependency>
  106. <groupId>org.hsqldb</groupId>
  107. <artifactId>hsqldb</artifactId>
  108. <version>2.2.8</version>
  109. </dependency>
  110. <dependency>
  111. <groupId>antlr</groupId>
  112. <artifactId>antlr</artifactId>
  113. <version>2.7.6</version>
  114. </dependency>
  115. <dependency>
  116. <groupId>commons-collections</groupId>
  117. <artifactId>commons-collections</artifactId>
  118. <version>3.1</version>
  119. </dependency>
  120. <dependency>
  121. <groupId>dom4j</groupId>
  122. <artifactId>dom4j</artifactId>
  123. <version>1.6.1</version>
  124. </dependency>
  125. <dependency>
  126. <groupId>javassist</groupId>
  127. <artifactId>javassist</artifactId>
  128. <version>3.4.GA</version>
  129. </dependency>
  130. <dependency>
  131. <groupId>javax.transaction</groupId>
  132. <artifactId>jta</artifactId>
  133. <version>1.1</version>
  134. </dependency>
  135. <dependency>
  136. <groupId>org.slf4j</groupId>
  137. <artifactId>slf4j-api</artifactId>
  138. <version>1.5.6</version>
  139. </dependency>
  140. </dependencies>
  141. </project>

JPA 示例

随时给我您的疑问和建议。

祝您学习愉快!