JPA
(즉, ORM) 기술이 이에 해당해요.SQL Mapper
역시 해당 계층을 위해 사용되는 Persistence Framework에 해당해요.JDBC
, Mybatise
가 이에 해당해요.그 전에 JPA를 사용하면 사용할 수 있는 테스트 작업과 슬라이싱 테스트라는 것을 나눠 알아볼 것인데! 이를 어떻게 사용할 수 있는지를 잘 보고 실제 기능 테스트에 적용해주세요!
public interface OrederRepository extends JpaRepository<Oreder, Long> {
}
JpaRepository
인터페이스를 상속 받아 사용하는 구조예요.JpaRepository<Entity, Entity PK Type>
형식으로 넣어주셔야 해요.package com.example.springbootjpatest.repository;
@DataJpaTest
public class OrderRepositoryTests {
}
@DataJpaTest
해당 애너테이션은 JPA가 제공하는 레포지토리를 사용할 경우에 사용이 가능한데요자바 코드 작성 패키지
테스트 코드 작성 패키
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private String email;
// Getters and setters
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<User, Long> {
}
public class DummyUser {
public static User createDummyUser(String username, String email) {
User user = new User();
user.setUsername(username);
user.setEmail(email);
return user;
}
}
DummyUser
객체를 반환하는 메서드를 만들어두고 객체 생성 없이 static 메서드로 바로 사용이 가능하는 것이 더 높은 재사용성을 보장해요.User user = DummyUser.createDummyUser("testuser", "test@example.com");
이런 식으로 말이죠!import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
@DataJpaTest
public class UserRepositoryTests {
@Autowired
private UserRepository userRepository;
private User user1;
private User user2;
@BeforeEach
public void setUp() {
user1 = DummyUser.createDummyUser("user1", "user1@example.com");
user2 = DummyUser.createDummyUser("user2", "user2@example.com");
userRepository.save(user1);
userRepository.save(user2);
}
@DisplayName("회원 저장 테스트")
@Test
public void testSaveUser() {
//given
User user = DummyUser.createDummyUser("testuser", "test@example.com");
//when
userRepository.save(user);
//then
assertNotNull(user.getId()); // ID가 생성되었는지 확인
}
@Test
public void testFindAllUsers() {
List<User> users = userRepository.findAll();
assertEquals(2, users.size()); // 저장된 사용자 수가 맞는지 확인
}
}
DataJpaTest
@BeforeEach
@DisplayName
@DataJpaTest
public class UserRepositoryTests {
//생략
@Test
public void testSaveUser() {
//given
User user = DummyUser.createDummyUser("testuser", "test@example.com");
//when
userRepository.save(user);
//then
assertNotNull(user.getId()); // ID가 생성되었는지 확인
}
}
given
, when
, then
으로 이루어져 있어요.given
when
Test slicing is about segmenting the ApplicationContext that is created for your test. Typically, if you want to test a controller using MockMvc, surely you don’t want to bother with the data layer. Instead you’d probably want to mock the service that your controller uses and validate that all the web-related interaction works as expected.
테스트 슬라이싱(Test slicing)은 테스트할 때 생성되는 ApplicationContext를 세분화하는 것입니다. 일반적으로 MockMvc를 사용하여 컨트롤러를 테스트하려는 경우 데이터 레이어에 신경 쓸 필요가 없습니다. 대신에 컨트롤러가 사용하는 서비스를 모킹하고, 모든 웹 관련 상호 작용이 예상대로 작동하는지 확인할 것입니다.
즉, 테스트 슬라이싱은 테스트를 세분화하여 필요한 부분만 포함하도록 합니다. 데이터 레이어, 서비스 레이어, 웹 레이어 등과 같은 다양한 레이어를 포함할 수 있습니다. 예를 들어, 컨트롤러를 테스트할 때 실제 데이터베이스에 액세스할 필요 없이 서비스 레이어만 모킹하여 웹 상호 작용을 테스트할 수 있습니다.
이를 통해 테스트를 더욱 효율적으로 작성하고, 필요한 부분만 집중적으로 테스트할 수 있습니다. 결과적으로 테스트 코드의 실행 속도가 향상되고 테스트 관리가 더욱 용이해집니다.
@SpringBootTest
어노테이션을 이용하면 모든 테스트를 할 수 있는데 왜 레이어별로 잘라서 테스트할까?
@SpringBootTest
어노테이션의 단점은 아래와 같아요.
Bean
을 로드하기 때문에 시간이 오래걸리고 무거워요.따라서 @SpringBootTest
어노테이션은 어플리케이션 컨텍스트 전체를 사용하는 통합 테스트에 사용돼야 합니다.
스프링 부트는 자동 설정의 일부만을 테스트 슬라이스로 가져와서 테스트에 활용할 수 있도록 다양한 테스트 어노테이션을 제공해줘요.
아래는 대표적인 슬라이스 테스트 어노테이션입니다.
@WebMvcTest
@WebFluxTest
@DataJpaTest
@JsonTest
@RestClientTest
public interface QueryRoleRepository {
Optional<Role> findById(Long id);
}
@RequiredArgsConstructor
@Repository
public class QueryRoleRepositoryImpl implements QueryRoleRepository {
private final JPAQueryFactory jpaQueryFactory;
@Override
public Optional<Role> findById(Long id) {
QRole role = QRole.role;
return Optional.ofNullable(
jpaQueryFactory
.selectFrom(role)
.where(role.roleId.eq(id))
.fetchOne()
);
}
}
@DataJpaTest
를 이용해서 테스트하기 위해선 슬라이스 테스트를 진행해야 해요.package com.fisa.infra.role.repository.querydsl;
import static org.assertj.core.api.Assertions.*;
import java.util.Optional;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import com.fisa.infra.role.domain.entity.Role;
import com.fisa.infra.role.repository.querydsl.inter.QueryRoleRepository;
import jakarta.persistence.EntityManager;
import jakarta.transaction.Transactional;
@SpringBootTest
@Transactional
class RoleRepositoryTest {
@Autowired
EntityManager entityManager;
@Autowired
QueryRoleRepository roleRepository;
private Role user;
private Role admin;
@BeforeEach
void setUp() {
admin=Role.createRole("ROLE_ADMIN");
user=Role.createRole("ROLE_USER");
}
@Test
@DisplayName("id값으로 Role 찾아오는 테스트")
void findById() {
entityManager.persist(admin);
entityManager.persist(user);
Optional<Role> opAdmin = roleRepository.findById(1L);
Optional<Role> opUser = roleRepository.findById(2L);
assertThat(opAdmin.isEmpty()).isFalse();
assertThat(opUser.isEmpty()).isFalse();
assertThat(opAdmin.get().getName()).isEqualTo("ROLE_ADMIN");
assertThat(opUser.get().getName()).isEqualTo("ROLE_USER");
}
}
@SpringBootTest
@Transactional
@DataJpaTest
사용을 위해서 아래와 같이 테스트 설정 파일을 수정하고 사용합니다.설정 파일 건들이기
package com.fisa.infra.config;
@TestConfiguration
public class QuerydslTestConfig {
@PersistenceContext
private EntityManager entityManager;
@Bean
public JPAQueryFactory jpaQueryFactory(){
return new JPAQueryFactory(this.entityManager);
}
}
@TestConfiguration
테스트 코드 작성하기
package com.fisa.infra.account.repository.querydsl;
import java.util.Optional;
import static org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase.Replace.NONE;
@AutoConfigureTestDatabase(replace = NONE)
@Import(QuerydslTestConfig.class)
@DataJpaTest
class QueryAccountRepositoryImplTest {
private QueryAccountRepository testQueryAccountRepository;
@Autowired
private JPAQueryFactory jpaQueryFactory;
private Account account;
@BeforeEach
void setUp() {
//given
account = DummyAccount.dummy();
//여기서 이제 실제 구현체를 생성해줘서 해당 작업을 진행하는 방법
testQueryAccountRepository = new QueryAccountRepositoryImpl(jpaQueryFactory);
}
@Test
void queryFindAccountByLoginId() {
//given
//when
Optional<AccountDTO> op = testQueryAccountRepository.queryFindAccountByLoginId("test");
Assertions.assertThat(op.isEmpty()).isFalse();
}
}
@Import(TestConfig.class)
를 이용해주어야 합니다.설정 파일 건들이기
package com.fisa.infra.config;
@TestConfiguration
public class QuerydslTestConfig {
@PersistenceContext
private EntityManager entityManager;
@Bean
public QueryAccountRepositoryImpl testQueryAccountRepository() {
return new QueryAccountRepositoryImpl(new JPAQueryFactory(entityManager));
}
}
테스트 코드 작성하기
package com.fisa.infra.account.repository.querydsl;
@AutoConfigureTestDatabase(replace = NONE)
@Import(QuerydslTestConfig.class)
@DataJpaTest
class QueryAccountRepositoryImplTest {
@Autowired
private QueryAccountRepository testQueryAccountRepository;
private Account account;
@BeforeEach
void setUp() {
account = DummyAccount.dummy();
}
@Test
void queryFindAccountByLoginId() {
//given
//when
Optional<AccountDTO> op = testQueryAccountRepository.queryFindAccountByLoginId("test");
//then
Assertions.assertThat(op.isEmpty()).isFalse();
}
}