初めてjavaでテストコードを書くという経験をし、わからないことだらけで
書き方から分からず大変だったので自分の備忘録や復習のため、記事を書きました。
同じような方の少しでも参考になればと思います。
※必要なプラグイン、設定、importは記載していません。
開発環境:
intellij / java17 / junit5
テストの実行方法
アサーション
// テスト対象のクラス
public class CalculatorApp {
// 除算メソッド
public int testDivision(int dividend ,int divisor) {
return dividend/divisor;
}
}
// テスト実装クラス
class CalculatorAppTest {
//テストメソッド
@Test
@DisplayName("除算メソッドに割り切れる整数が引数で渡された時の検証")
void testDivision() {
var calculator = new CalculatorApp();
var result = calculator.testDivision(4,2);
// アサーション
assertEquals(2,result);
}
}
テストしたいメソッドが正常に動作するかどうか、および正しい結果を返すかどうかを確認するために、assertを使用する。与えられた入力パラメータで条件を評価する。
アサーションの種類
テストメソッド内部の構造化、パターン
AAA(トリプルエー)
テストコードを読みやすくするための指針です。AAA は arrange, act, assert の略語で、arrange は『準備』、act は『実行』、assertは『検証』を表します。
- Arrange
- テストメソッドを呼び出すためのインスタンス生成、入力パラメータの準備
- Act
- テスト中のメソッドを呼び出し、実際の結果を返す
- Assert
- 保持された値を検証する
class CalculatorAppTest {
@Test
@DisplayName("除算メソッドに割り切れる整数が引数で渡された時の検証")
void testDivision() {
// Arrange テストメソッドを呼び出すためのインスタンス生成、入力パラメータの準備
var calculator = new CalculatorApp();
var dividend = 4;
var divisor = 2;
var expectedResult = 2;
// Act テスト中のメソッドを呼び出し、実際の結果を返す
var actualResult = calculator.testDivision(dividend,divisor);
// Assert 保持された値を検証する
assertEquals(expectedResult,actualResult);
}
}
テストライフサイクル
-
BeforeAll
全てのテストメソッドの前にメソッドを1回だけ実行させる -
AfterAll
全てのテストメソッド完了後にのみ1回だけ実行される -
BeforeEach
付与されているメソッドが各テストの実行前に実行される -
AfterEach
付与されているメソッドが各テストの完了後に実行される
この中でもよく使用するのは@BeforeEach
例外アサーション
class CalculatorAppTest {
CalculatorApp calculator;
@Test
@DisplayName("除算メソッドに割り切れない整数が引数で渡された時、例外がスローされる検証")
void testDivision_error() {
calculator = new CalculatorApp();
// Arrange
var dividend = 4;
var divisor = 0;
var exceptionMessage = "/ by zero";
// Act & Assert
// 例外が発生することを確認する例外アサート
var arithmeticException = assertThrows(ArithmeticException.class, () -> {
// Act
calculator.testDivision(dividend,divisor);
});
// Assert
assertEquals(exceptionMessage, arithmeticException.getMessage());
}
}
assertThrowsの第1引数で「発生するであろう例外」のクラスを指定し、第2引数(ラムダ式)でテスト対象の処理を実行する。
モックオブジェクトの作成
@ExtendWith(MockitoExtension.class) // このクラスでMockitoアノテーションを使えるようになる
public class UserServiceTest {
// 処理省略
}
@ExtendWith(MockitoExtension.class)
このクラスでMockitoアノテーションを使えるようになる
@ExtendWith(MockitoExtension.class)
public class UserServiceTest {
@InjectMocks
UserService userService;
@Mock
UsersRepository userRepository;
// 処理省略
}
@Mock
モック化するクラスにつける
@InjectMocks
テスト対象クラスにモックを注入
@ExtendWith(MockitoExtension.class)
public class UserServiceTest {
// 上記の処理省略
@DisplayName("ユーザー作成の検証")
@Test
void testCreateUser() {
// Arrange
Mockito.when(usersRepository.save(any(User.class)).thenReturn(true);
// Act
var user = userService.createUser(firstName, lastName, email, password, repeatPassword)
// Assert
assertNotNull(user);
assertEquals(firstName, user.getFirstName());
assertEquals(lastName, user.getLastName());
assertEquals(email, user.getEmail());
assertNotNull(user.getId());
verify(usersRepository, Mockito.times(1)).save(any(User.class));
}
}
モック化したクラスのメソッドを実行したときの戻り値を設定する
- 書き方は2パターン
when(モックインスタンス.メソッド(引数)).thenReturn(戻り値);
doReturn(戻り値).when(モックインスタンス).メソッド(引数);
メソッド呼び出しの検証
@ExtendWith(MockitoExtension.class)
public class UserServiceTest {
// 上記の処理省略
@Test
@DisplayName("ユーザー作成の検証")
void testCreateUser() {
// Arrange
// モック化したクラスのメソッドを実行したときの戻り値を設定する
// Mockito.when(モックインスタンス.メソッド(引数)).thenReturn(戻り値)
Mockito.when(usersRepository.save(any(User.class)).thenReturn(true);
// Act
var user = userService.createUser(firstName, lastName, email, password, repeatPassword)
// Assert
// メソッド呼び出しの検証
verify(usersRepository, times(1)).save(any(User.class));
// 一度もメソッド呼び出しがされない検証。never()はtimes(0)と同じ
// verify(usersRepository, never()).save(any(User.class));
メソッドが呼ばれた回数をテストする
verify(モックオブジェクト, times(回数)).テストするメソッド(引数);
JPAエンティティのテスト
@DataJpaTest
public class UserEntityTest {
// エンティティマネージャの代替オブジェクト、データ層をテストするときのクラス
// 情報を永続化し、すぐにデータベーステーブルと同期させることができる
@Autowired
private TestEntityManager testEntityManager;
@Test
void userEntitytest() {
// Arrange
var userEntity = new UserEntity();
userEntity.setUserId(UUID.randomUUID().toString());
userEntity.setFirstName("yamada");
userEntity.setLastName("taro");
userEntity.setEmail("test@test.com");
userEntity.setPassword("pass");
// Act
// テストデータを設定
var resultUserEntity = testEntityManager.persistAndFlush(userEntity);
// Assert
assertTrue(resultUserEntity.getId() > 0);
assertEquals(userEntity.getUserId(), resultUserEntity.getUserId());
assertEquals(userEntity.getFirstName(), resultUserEntity.getFirstName());
assertEquals(userEntity.getLastName(), resultUserEntity.getLastName());
assertEquals(userEntity.getEmail(), resultUserEntity.getEmail());
assertEquals(userEntity.getPassword(), resultUserEntity.getPassword());
}
}
TestEntityManagerはEntityManagerの代わり
情報を永続化し、すぐにデータベーステーブルと同期させることができる
JPQLクエリ、ソースコードのテスト
// テスト対象のリポジトリ
@Repository
public interface UserRepository extends PagingAndSortingRepository<UserEntity, Long> {
@Query("select user from UserEntity user where user.email like %:emailDomain")
List<UserEntity> findUsersWithEmailEndingWith(@Param("emailDomain") String emailDomain);
}
//コード省略
@Autowired
private TestEntityManager testEntityManager;
private final String email = "test@test.com";
private final String email2 = "test@test.com";
@BeforeEach
void setup() {
// ユーザー作成
// first user
var userEntity = new UserEntity();
userEntity.setUserId(userId1);
userEntity.setEmail(email1);
userEntity.setPassword("12345678");
userEntity.setFirstName("tanaka");
userEntity.setLastName("ichiro");
// テストデータを設定
testEntityManager.persistAndFlush(userEntity);
// second user
var userEntity2 = new UserEntity();
userEntity2.setUserId(userId2);
userEntity2.setEmail(email2);
userEntity2.setPassword("abcdefgh");
userEntity2.setFirstName("takeda");
userEntity2.setLastName("hanako");
// テストデータを設定
testEntityManager.persistAndFlush(userEntity2);
}
@Test
void testfindUsersWithEmailEndingWith() {
// Arrange
var userEntity = new UserEntity();
userEntity.setUserId(UUID.randomUUID().toString());
userEntity.setEmail("test@gmail.com");
userEntity.setPassword("pass");
userEntity.setFirstName("yamada");
userEntity.setLastName("taro");
testEntityManager.persistAndFlush(userEntity);
var emailDomailName = "@gmail.com";
// Act
var users = useraRepository.findUsersWithEmailEndingWith(emailDomainName);
// Assert
// emailドメインが"@gmail.com"のユーザーは一件しか取得できないこと
Assertions.assertEquals(1,users.size());
//emailドメインが一致すること
Assertions.assertTrue(users.get(0).getEmail().endsWith(emailDomainName));
}