前言
1、場景
在寫DAO層的單元測試時,我們往往會遇到一個問題,測試用例所依賴的數據庫數據被修改或刪除了,或者在一個新的環境下所依賴的數據庫不存在,導致單元測試無法通過。在這種情況下,使用H2內存數據庫來模擬數據庫環境是一個很好的解決方案。
2、H2 特點
- 只有一個jar文件,適合作為嵌入式數據庫使用
- 支持標准SQL和JDBC
- 可以用於單元測試,啟動很快,每一個用例執行完會自動刪除內存中的數據
上代碼
使用maven工程來搭建測試環境,工程目錄如下:

1、核心依賴
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.0.RELEASE</version> </parent> <dependencies> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <scope>provided</scope> </dependency> </dependencies>
2、配置文件 application.yml
server: port: 18095 spring: application: name: service-h2 jpa: show-sql: true #打印sql datasource: url: jdbc:h2:mem:xwj_db;MODE=MySQL;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE #mem:連接到內存 driver-class-name: org.h2.Driver username: root password: 123456 data: classpath:db/data.sql #初始化表數據
參數說明:
- jdbc:h2:mem 使用h2的內存數據庫,還有file等其它方式
- xwj_db 數據庫名稱
- MODE=MySQL 以 MySQL 的模式運行
3、實體類
@Entity @Getter @Setter @ToString @NoArgsConstructor public class XwjUser implements Serializable { private static final long serialVersionUID = -2169427939264532306L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; /** 名稱 */ public String name; /** 年齡 */ private Integer age; private Date createDate; }
4、Repository
public interface UserRepository extends JpaRepository<XwjUser, Long>{ }
5、Service
@Service public class UserService { @Autowired private UserRepository userRepository; @Autowired private JdbcTemplate jdbcTemplate; public List<XwjUser> findAll() { return userRepository.findAll(); } public XwjUser save(XwjUser user) { return userRepository.save(user); } /** * 通過主鍵id查詢 */ public XwjUser findById(Long id) { Optional<XwjUser> optional = userRepository.findById(id); if (optional.isPresent()) { return optional.get(); } return null; } /** * 通過名稱查詢 */ public List<XwjUser> findByName(String name) { String sql = "select * from xwj_user where name like ?"; Object[] paramArr = new Object[1]; paramArr[0] = name + "%"; return jdbcTemplate.query(sql, paramArr, new BeanPropertyRowMapper<>(XwjUser.class)); } }
測試
1、在 src/main/resources 目錄下,創建一個目錄 /db 和腳本文件 data.sql :
insert into xwj_user (id, name, age, create_date) values(11,'張三','12','2020-04-17 00:00:00'); insert into xwj_user (id, name, age, create_date) values(12,'李四','13','2020-04-17 00:00:00'); insert into xwj_user (id, name, age, create_date) values(13,'王五','25','2020-04-17 00:00:00'); insert into xwj_user (id, name, age, create_date) values(14,'趙六','17','2020-04-17 00:00:00'); insert into xwj_user (id, name, age, create_date) values(15,'吉米','30','2020-04-17 00:00:00');
2、在 src/test/java 目錄下,創建一個測試類:
@RunWith(SpringRunner.class) @SpringBootTest public class H2Test { @Autowired private UserService userService; /** * 測試查詢所有 */ @Test public void testFind() { List<XwjUser> list = userService.findAll(); System.out.println("list: " + list); } /** * 測試新增+查詢 */ @Test public void testInsertAndFind() { // 1、新增 XwjUser user = new XwjUser(); user.setAge(19); user.setName("張三"); user.setCreateDate(new Date()); XwjUser newUser = userService.save(user); // 2、通過id查詢 Long id = newUser.getId(); System.out.println("findById: " + userService.findById(id)); // 3、模糊查詢(自定義sql) String name = "張"; System.out.println("findByName: " + userService.findByName(name)); } }
3、測試 H2Test#testFind 方法,日志如下:

4、測試 H2Test#testInsertAndFind 方法,日志如下:

踩坑
1、在配置文件不要配置 schema,也不要直接將 schema.sql 腳本直接放在 src/main/resources 目錄下,否則初始化數據腳本(即上面的data.sql)不會生效。
問題現象:執行方法的過程中沒有任何報錯,schema 和 data 腳本都會被執行(故意寫錯腳本會拋出異常)
問題原因:待進一步分析
