✅ この回で学べること
- Service層のJUnitテストの基本構成
- DAOやDBアクセスを含むServiceメソッドのテスト手法
- H2 Databaseを使ったインメモリテストの実装例
- モック化と実DBテストの使い分けパターン
🧩 事前に準備するもの
- JUnit(JUnit 5 or 4)
- H2 Database(組み込みDB)
- DAOやDBユーティリティの共通化済みの状態(Vol.10.3まで参照)
✅ テスト構成イメージ(単体テスト)
[Test] UserServiceTest
↓ 呼び出し
[Service] UserService
↓ 呼び出し
[DAO] UserDao(H2DB用の実装 or モック)
📘 例1:H2データベースを用いたテスト(実DBテスト)
✅ pom.xml(H2とJUnit依存追加)
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
✅ UserServiceTest.java
public class UserServiceTest {
private UserService userService;
private DataSource dataSource;
@Before
public void setUp() throws Exception {
// H2 in-memory DBの初期化
JdbcDataSource ds = new JdbcDataSource();
ds.setURL("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1");
ds.setUser("sa");
ds.setPassword("");
this.dataSource = ds;
// テーブル作成・テストデータ投入
try (Connection conn = ds.getConnection();
Statement stmt = conn.createStatement()) {
stmt.execute("CREATE TABLE users (user_id INT PRIMARY KEY, username VARCHAR(100), email VARCHAR(100))");
stmt.execute("CREATE TABLE user_roles (user_id INT, role VARCHAR(50))");
}
// DAOとServiceのセットアップ
UserDao userDao = new UserDaoImpl();
UserRoleDao userRoleDao = new UserRoleDaoImpl();
this.userService = new UserService(userDao, userRoleDao, dataSource);
}
@Test
public void testRegisterUser_success() throws Exception {
User user = new User(1, "testuser", "test@example.com");
userService.registerUser(user, "admin");
try (Connection conn = dataSource.getConnection();
PreparedStatement stmt = conn.prepareStatement("SELECT * FROM users WHERE user_id = ?")) {
stmt.setInt(1, 1);
ResultSet rs = stmt.executeQuery();
assertTrue(rs.next());
assertEquals("testuser", rs.getString("username"));
}
}
}
✅ Service層の例外テスト
@Test(expected = ServiceException.class)
public void testRegisterUser_rollbackOnError() throws Exception {
User user = new User(999, "errorUser", "invalid@example.com");
// エラー発生をシミュレーションするため、userRoleDao側にエラー仕込みなども可能
userService.registerUser(user, "invalid-role");
// 登録されていないことを確認(ロールバックされている)
try (Connection conn = dataSource.getConnection();
PreparedStatement stmt = conn.prepareStatement("SELECT * FROM users WHERE user_id = ?")) {
stmt.setInt(1, 999);
ResultSet rs = stmt.executeQuery();
assertFalse(rs.next());
}
}
✅ モックによるServiceテスト(Mockito使用)
H2を使わずにDAOをモックする場合:
@Test
public void testRegisterUser_withMockDao() throws Exception {
UserDao userDao = Mockito.mock(UserDao.class);
UserRoleDao userRoleDao = Mockito.mock(UserRoleDao.class);
DataSource ds = Mockito.mock(DataSource.class);
Connection conn = Mockito.mock(Connection.class);
when(ds.getConnection()).thenReturn(conn);
UserService userService = new UserService(userDao, userRoleDao, ds);
User user = new User(2, "mockuser", "mock@example.com");
userService.registerUser(user, "admin");
verify(userDao).insert(conn, user);
verify(userRoleDao).assignRole(conn, 2, "admin");
}
✅ テスト方針の選択ガイド
方法 | 特徴 | 向いているケース |
---|---|---|
H2 DBを使う | 実際のSQLとDB動作を確認できる | SQL検証やトランザクション検証 |
Mockitoでモック化 | DB依存なしで高速テスト | 業務ロジックの分岐やエラー処理テスト中心の場合 |
✅ 今回のまとめ
項目 | ポイント |
---|---|
H2使用 | 実DBを使った統合に近い単体テストが可能 |
モック使用 | DBに依存せずService層のロジックのみ検証できる |
トランザクション・例外 | Service層の責務としてテスト対象に含めるべき |