[Trouble Shooting] ItemTest ํ ์คํธ์ฝ๋ ๊ด๋ จ ํธ๋ฌ๋ธ ์ํ
์ด๋ฒ ํ๋ฌ์ค์ฃผ์ฐจ ๊ฐ์ธ๊ณผ์ ๋ ์ง๊ธ๊น์ง ๋ฐฐ์ด JPA์ฌํ, ํ ์คํธ ์ฝ๋, ์ฑ๋ฅ์ต์ ํ ๊ฐ๋ ๋ค์ ๊ธฐ๋ฐ์ผ๋ก ์งํ๋์๋ค.
๊ณผ์ ๋ฅผ ์งํํ๋ ์ค ํ ์คํธ ์ฝ๋ ์คํ ๊ณผ์ ์์ ๋ฐ์ํ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ๋ด์ฉ์ ์ ๋ฆฌํด๋ณด๊ณ ์ ํ๋ค.
1. ๋ฌธ์ ์ํฉ
๐ ItemTest ํด๋์ค์์ ์์ธ ๋ฐ์ ํ ์คํธ ์คํจ
item.setStatus(null);
- ItemTest ํด๋์ค์์ status ํ๋๋ฅผ null๋ก ์ค์ ํ๋ฉด PersistenceException์ด ๋ฐ์ํ ๊ฒ์ผ๋ก ์์ํ๋ค.
- ํ์ง๋ง ํ ์คํธ ์คํ ๊ฒฐ๊ณผ, AssertionError๊ฐ ๋ฐ์ํ๋ค.[๋๋ณด๊ธฐ ์ฐธ๊ณ ]
๐ข [๋ฌธ์ ์ฝ๋] : ItemTest ํด๋์ค
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class ItemTest {
@Autowired
private ItemRepository itemRepository;
@Test
@DisplayName("status ๊ฐ์ด null์ผ ๊ฒฝ์ฐ ์์ธ ๋ฐ์ ํ์ธ ")
// status ๊ฐ์ด ์๋ ๊ฒฝ์ฐ ํ
์คํธ ์งํ
void statusIsEmpty_shouldFail() {
Item item = new Item();
item.setName("Test Item");
item.setDescription("Test Description");
item.setStatus(null);
// ์ด๋ค ์ฝ๋๊ฐ ์คํ๋ ๋ ์๋ฌ๊ฐ ๋ฐ์ํ๋์ง ํ์ธ
assertThatThrownBy(() -> itemRepository.saveAndFlush(item))
.isInstanceOf(PersistenceException.class);
}
๐จ [์๋ฌ ๋ฉ์ธ์ง] ํ ์คํธ ์คํ ๊ฒฐ๊ณผ
java.lang.AssertionError:
Expecting code to raise a throwable.

2. ์์ธ ๋ถ์
1) Hibernate์ ๊ธฐ๋ณธ๊ฐ ์ค์ ๋ฌธ์
- Item ํด๋์ค์ status ํ๋๋ ๋ค์๊ณผ ๊ฐ์ด ์ค์ ๋์ด ์์๋ค.
@Column(nullable = false, columnDefinition = "varchar(20) default 'PENDING'")
private String status;
โ ๋ฌธ์ ์
- @Column(์๋ต....)์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ ์ด๋ธ์ ์์ฑํ ๋ ์ ์ฉ๋๋ SQL ์ ์ฝ์กฐ๊ฑด์ด๋ค.
- Hibernate๋ JPA๋ ๊ธฐ๋ณธ๊ฐ์ ์ ์ฉํ์ง ์๋๋ค.
- ๋ฐ๋ผ์, status ํ๋๊ฐ์ null์ด ์ค์ ๋ ์ํ ๊ทธ๋๋ก ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ๋ฌ๋์ด PersistenceException์ด ๋ฐ์ํ์ง ์๊ณ ๊ทธ๋๋ก ์ ๋ฌ๋์๋ค.
2) @DataJpaTest์ ํน์ฑ ๊ฐ๊ณผ
- @DataJpaTest๋ JPA Repository๊ธฐ๋ฅ์ ํ ์คํธํ๋๋ฐ ์ต์ ํ๋ ์ด๋ ธํ ์ด์ ์ด๋ค.
- ํ์ง๋ง, ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฝ์กฐ๊ฑด์ ๊ฒ์ฆํ๋ ๊ฒ์ด ์๋ ์ง์ SQL์ ์คํ์ ๊ฒ์ฆํ๊ธฐ์๋ ์ ํฉํ์ง ์์๋ค.
3. ํด๊ฒฐ๋ฐฉ์
ItemTest ํด๋์ค์์ JPA Repository๋ฅผ ์ฌ์ฉํด status ํ๋์ null ๊ฐ์ ์ค์ ํ ๋ค ์์ธ๊ฐ ๋ฐ์ํ๋์ง ๊ฒ์ฆํ๋ ค๊ณ ํ๋ค.
ํ์ง๋ง Repository ํ ์คํธ๋ง์ผ๋ก๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฝ ์กฐ๊ฑด ์๋ฐ ์ฌ๋ถ๋ฅผ ๋ช ํํ๊ฒ ํ ์คํธํ๊ธฐ ์ด๋ ค์
์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด JPQL๋ก ์ฟผ๋ฆฌ๋ฅผ ์ง์ ์คํํ๋ ItemJPQLTest ํด๋์ค๋ฅผ ์๋ก ์์ฑํ์ฌ ์งํํ๋ค.
ItemJPQLTest ํด๋์ค๋ EntityManager๋ฅผ ์ฌ์ฉํ์ฌ status ํ๋๋ฅผ ๊ฐ์ ๋ก null๋ก ์ค์ ํ๋ JPQL ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ๊ณ ์คํํ๋ค.
๊ฐ์ ๋ก null์ ์ค์ ํ๊ธฐ ๋๋ฌธ์ NOT NULL ์ ์ฝ ์กฐ๊ฑด์ด ์๋ํ์ฌ ์์ธ๊ฐ ๋ฐ์ํ๋์ง ์ ํํ๊ฒ ๊ฒ์ฆํ ์ ์์๋ค.
// ๊ฐ์ null ์ ์ฉ
assertThatThrownBy(() -> {
int updatedCount = entityManager.createQuery(
"UPDATE Item i SET i.status = null WHERE i.id = :id")
.setParameter("id", item.getId())
.executeUpdate(); // ์ฟผ๋ฆฌ ์คํ
}).isInstanceOf(PersistenceException.class);
๐ JPQL๊ณผ EntityManager์ ์ฌ์ฉํ ์ด์ ๋?
์์์ ๋งํ๋ฏ์ด Hibernate๊ฐ Repository๋ฅผ ํตํด ์ ์ฅํ ๋ ๊ธฐ๋ณธ๊ฐ์ด๋ ์ ์ฝ ์กฐ๊ฑด์ ์ ์ฉํ์ง ์๊ธฐ ๋๋ฌธ์ด๋ค.
๋๋ฌธ์ ์ง์ ์ฟผ๋ฆฌ๋ฅผ ์คํํด์ผ NOT NULL๊ณผ ๊ฐ์ ์ ์ฝ ์กฐ๊ฑด์ด ์ ๋๋ก ์๋ํ๋์ง ํ์ธํ ์ ์์ ๊ฒ ๊ฐ์๋ค.
๐ [์ฑ๊ณต ์ฝ๋] : ItemJPQLTest ํด๋์ค
@ActiveProfiles("test")
@SpringBootTest
@Transactional
class ItemJPQLTest {
@PersistenceContext
private EntityManager entityManager;
@Test
@DisplayName("JPQL UPDATE๋ฅผ ์ด์ฉํ์ฌ status๋ฅผ null๋ก ์ค์ ์ ์์ธ ๋ฐ์ ํ์ธ")
void updateStatusToNull_shouldNull() {
Item item = new Item();
item.setName("Test Item");
item.setDescription("Test Description");
item.setStatus("ACTIVE");
entityManager.persist(item);
entityManager.flush(); // ๊ฐ์ ๋ฐ์
// ๊ฐ์ null ์ ์ฉ
assertThatThrownBy(() -> {
int updatedCount = entityManager.createQuery(
"UPDATE Item i SET i.status = null WHERE i.id = :id")
.setParameter("id", item.getId())
.executeUpdate(); // ์ฟผ๋ฆฌ ์คํ
}).isInstanceOf(PersistenceException.class);
}
}
4. ๊ฒฐ๊ณผ
PersistenceException์ด ์ ์์ ์ผ๋ก ์๋ํ์ฌ ํ ์คํธ๊ฐ ํต๊ณผ๋์๋ค.

5. ๋ฐฐ์ธ ์
์... ์ฒ์์ ํ ์คํธ์ฝ๋ ํน๊ฐ์ ๋ณด๊ณ ๋ฌด์์ @DataJpaTest๋ฅผ ์ฌ์ฉํ๊ณ , JPA Repository ํ ์คํธ์ ํนํ๋ ์ด๋ ธํ ์ด์ ์ด๋ผ๊ณ ํ๋ค.(์ฒซ ๋จ์ถ๋ถํฐ ์๋ชป ๋ง์ท๋ ๊ฒ์ด๋ค.)
@DataJpaTest ์ด๋ ธํ ์ด์ ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ์ํธ์์ฉ์ ์ต์ํํ๊ณ Repository ๊ธฐ๋ฅ๋ง ๊ฒ์ฆํ๋๋ฐ ์ฃผ๋ก ์ฌ์ฉ๋๋ค๊ณ ํ๋ค.
EntityManager์ JPQL์ ์ฌ์ฉํ๋ฉด ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ ๋ฒจ์์์ ์ ์ฝ ์กฐ๊ฑด ๊ฒ์ฆ์ ๋ช ํํ ํ ์คํธํ ์ ์์๋ค.