@DataJpaTest
를 사용하여 진행한다.@DataJpaTest
: jpa component에 초점을 맞춘 jpa를 사용할 때 이를 테스트하기 위해 만들어진 애노테이션으로 레포지토리와 관련된 테스트는 이를 이용해서 진행할 것이다.
📌출처: https://jesusvalerareales.medium.com/testing-with-test-doubles-7c3abb9eb3f2
package com.boostore.version_1.member.repository;
import com.boostore.version_1.member.dummy.MemberDummy;
import com.boostore.version_1.member.dummy.MembershipDummy;
import com.boostore.version_1.member.entity.Member;
import com.boostore.version_1.member.entity.Membership;
import com.boostore.version_1.member.repository.JpaMemberRepository;
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.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;
import java.util.List;
import java.util.Optional;
import static org.assertj.core.api.Assertions.assertThat;
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class JpaMemberRepositoryTest {
@Autowired
JpaMemberRepository jpaMemberRepository;
@Autowired
TestEntityManager testEntityManager;
private Member member;
private Membership membership;
@BeforeEach
void setUp(){
member = MemberDummy.dummy();
membership = MembershipDummy.dummy();
}
@DisplayName("회원 저장 테스트")
@Test
void testSaveMember(){
//given
testEntityManager.persist(membership);
//when
Member savedMember = jpaMemberRepository.save(member);
//then
assertThat(savedMember).isNotNull();
assertThat(savedMember.getMembership().getGrade()).isEqualTo(membership.getGrade());
}
@DisplayName("id로 해당하는 회원 조회 테스트")
@Test
void testMemberFindById(){
//given
testEntityManager.persist(membership);
Member savedMember = testEntityManager.persist(member);
//when
Optional<Member> optionalMember = jpaMemberRepository.findMemberByMemberId(savedMember.getMemberId());
//then
assertThat(optionalMember.isPresent());
assertThat(optionalMember.get().getName()).isEqualTo(savedMember.getName());
}
@DisplayName("모든 회원 조회 테스트")
@Test
void testMemberFindAll(){
//given
testEntityManager.persist(membership);
Member savedMember = testEntityManager.persist(member);
//when
List<Member> members = jpaMemberRepository.findAll();
//then
assertThat(members).isNotNull();
assertThat(members.size()).isOne();
}
/** 회원가입과 관련된 service 레이어에서 사용될 메소드를 검증하기 위한 테스트 */
@DisplayName("해당 id를 가진 회원이 있는지 조회하는 테스트")
@Test
void testExistMemberById() {
Member savedMember = testEntityManager.persist(member);
boolean resultTrue = jpaMemberRepository.existsMemberById(savedMember.getId());
assertThat(resultTrue).isTrue();
boolean resultFalse = jpaMemberRepository.existsMemberById("test");
assertThat(resultFalse).isFalse();
}
@DisplayName("해당 닉네임으로 가입한 회원이 있는지 조회하는 테스트")
@Test
void testExistMemberByNickname() {
Member savedMember = testEntityManager.persist(member);
boolean resultTrue = jpaMemberRepository.existsMemberByNickname(savedMember.getNickname());
assertThat(resultTrue).isTrue();
boolean resultFalse = jpaMemberRepository.existsMemberByNickname("test");
assertThat(resultFalse).isFalse();
}
@DisplayName("해당 이메일로 가입한 회원이 있는지 조회하는 테스트")
@Test
void testExistMemberByEmailAddress() {
Member savedMember = testEntityManager.persist(member);
boolean resultTrue = jpaMemberRepository.existsMemberByEmailAddress(savedMember.getEmailAddress());
assertThat(resultTrue).isTrue();
boolean resultFalse = jpaMemberRepository.existsMemberByEmailAddress(savedMember.getEmailAddress());
assertThat(resultFalse).isTrue();
}
}
더미 객체
package com.boostore.version_1.member.dummy;
import com.boostore.version_1.member.domain.GenderCode;
import com.boostore.version_1.member.entity.Member;
import com.boostore.version_1.member.entity.Membership;
import java.time.LocalDateTime;
public class MemberDummy {
public static Member dummy(){
String id = "yeomyaloo";
String name = "yaloo";
String password = "1234";
String email = "test@test.com";
String birthday = "20001111";
String phoneNumber = "010-0000-0000";
String address = "ㅇㅇ시 ㅇㅇ구";
// memberCreatedAt gradeDummy
return Member.builder()
.membership(MembershipDummy.dummy())
.id(id)
.nickname(name)
.name(name)
.genderCoder(GenderCode.FEMALE)
.birthday(birthday)
.password(password)
.phoneNumber(phoneNumber)
.address(address)
.emailAddress(email)
.memberCreatedAt(LocalDateTime.now())
.build();
}
public static Member dummy(Membership membership) {
String id = "yeomyaloo";
String name = "yaloo";
String password = "1234";
String email = "test@test.com";
String birthday = "20001111";
String phoneNumber = "010-0000-0000";
String address = "ㅇㅇ시 ㅇㅇ구";
// memberCreatedAt gradeDummy
return Member.builder()
.membership(membership)
.id(id)
.nickname(name)
.name(name)
.genderCoder(GenderCode.FEMALE)
.birthday(birthday)
.password(password)
.phoneNumber(phoneNumber)
.address(address)
.emailAddress(email)
.memberCreatedAt(LocalDateTime.now())
.build();
}
}
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package org.springframework.boot.test.autoconfigure.orm.jpa;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.test.autoconfigure.OverrideAutoConfiguration;
import org.springframework.boot.test.autoconfigure.core.AutoConfigureCache;
import org.springframework.boot.test.autoconfigure.filter.TypeExcludeFilters;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.properties.PropertyMapping;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.core.annotation.AliasFor;
import org.springframework.data.repository.config.BootstrapMode;
import org.springframework.test.context.BootstrapWith;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.transaction.annotation.Transactional;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@BootstrapWith(DataJpaTestContextBootstrapper.class)
@ExtendWith({SpringExtension.class})
@OverrideAutoConfiguration(
enabled = false
)
@TypeExcludeFilters({DataJpaTypeExcludeFilter.class})
@Transactional
@AutoConfigureCache
@AutoConfigureDataJpa
@AutoConfigureTestDatabase
@AutoConfigureTestEntityManager
@ImportAutoConfiguration
public @interface DataJpaTest {
String[] properties() default {};
@PropertyMapping("spring.jpa.show-sql")
boolean showSql() default true;
@PropertyMapping("spring.data.jpa.repositories.bootstrap-mode")
BootstrapMode bootstrapMode() default BootstrapMode.DEFAULT;
boolean useDefaultFilters() default true;
ComponentScan.Filter[] includeFilters() default {};
ComponentScan.Filter[] excludeFilters() default {};
@AliasFor(
annotation = ImportAutoConfiguration.class,
attribute = "exclude"
)
Class<?>[] excludeAutoConfiguration() default {};
}
@ExtendWith
애노테이션을 사용하여 테스트를 진행한다. 이때 @WebMvcTest의 경우 @Service, @Repository, @Component등의 요소들은 테스트할 수 없기 때문에 Service의 경우라면 @ExtendWith를 사용해서 진행하자.@Controller
, @RestController
컨트롤러의 경우엔 @WebMvcTest
애노테이션을 사용해서 컨트롤러부만 테스트를 진행한다. 이때 위의 설명과 같이 서비스, 레포지토리, 컴포넌트 같은 요소들은 따로 이 애노테이션을 사용해서 테스트가 불가하다. @WebMvcTest
의 경우엔 단위테스트로 비교적 가볍게 테스트가 가능하다.package com.boostore.version_1.member.controller;
import com.boostore.version_1.member.dto.request.MemberCreateRequest;
import com.boostore.version_1.member.dto.response.MemberCreateResponse;
import com.boostore.version_1.member.entity.Member;
import com.boostore.version_1.member.entity.Membership;
import com.boostore.version_1.member.entity.Role;
import com.boostore.version_1.member.service.inter.MemberService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import javax.print.attribute.standard.MediaSize;
import static org.hamcrest.Matchers.equalTo;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@WebMvcTest(MemberRestController.class)
class MemberRestControllerTest {
private final String NAME = "yaloo";
private final String NICKNAME = "yaloo";
private final String ID = "yaloo";
private final String PHONENUMBER = "01000009999";
private final String INVALID_PASSWORD = "abcde";
private final String VALID_PASSWORD = "asdad122ed@";
private final String BIRTH = "19960320";
private final String EMAIL = "test@test.com";
private final String GENDER = "FEMALE";
private final String ROLE_MEMBER = "ROLE_MEMBER";
@Autowired
ObjectMapper objectMapper;
@Autowired
private MockMvc mockMvc;
@MockBean
private MemberService memberService;
private Member member;
private MemberCreateRequest createRequest;
private MemberCreateResponse createResponse;
@BeforeEach
void setUp() {
Long memberId = 1L;
Long roleId = 1L;
member = Member.builder()
.memberId(memberId)
.name(NAME)
.nickname(NICKNAME)
.id(ID)
.build();
Role role = Role.builder()
.roleId(roleId)
.roleName(ROLE_MEMBER)
.build();
createResponse = MemberCreateResponse.fromEntity(member, role);
}
@DisplayName("회원 등록시 사용자로부터 받은 데이터가 검증 조건에 맞지 않은 경우 요청에 실패하도록 하는 테스트")
@Test
@WithMockUser
void signUpMember_invalidInputData() throws Exception {
//given
MemberCreateRequest request = new MemberCreateRequest();
Mockito.when(memberService.createMember(any())).thenReturn(createResponse);
//when
ResultActions perform = mockMvc.perform(post("/api/service/members/sign-up").with(csrf())
.contentType(MediaType.APPLICATION_JSON).content(objectMapper.writeValueAsString(request)));
//then
perform.andDo(print()).andExpect(status().isBadRequest());
verify(memberService, never()).createMember(any());
}
@DisplayName("회원 가입 테스트에 시 정규 표현식에 어긋나는 경우 요청 실패 테스트")
@Test
@WithMockUser
void signUpMember_invalidRegex() throws Exception {
//given
MemberCreateRequest request = new MemberCreateRequest(
ID,
NICKNAME,
NAME,
GENDER,
BIRTH,
INVALID_PASSWORD,
PHONENUMBER,
EMAIL
);
Mockito.when(memberService.createMember(any())).thenReturn(createResponse);
//when
ResultActions perform = mockMvc.perform(post("/api/service/members/sign-up").with(csrf())
.contentType(MediaType.APPLICATION_JSON).content(objectMapper.writeValueAsString(request)));
//then
perform.andDo(print()).andExpect(status().isBadRequest());
verify(memberService, never()).createMember(any());
}
@DisplayName("회원 가입 성공 테스트")
@Test
@WithMockUser
void signUpMember() throws Exception {
//given
MemberCreateRequest request = new MemberCreateRequest(
ID,
NICKNAME,
NAME,
GENDER,
BIRTH,
VALID_PASSWORD,
PHONENUMBER,
EMAIL
);
Mockito.when(memberService.createMember(any())).thenReturn(createResponse);
Membership membership = Membership.createMembership();
Member member = request.toEntity(membership);
//when
ResultActions perform = mockMvc.perform(post("/api/service/members/sign-up").with(csrf())
.contentType(MediaType.APPLICATION_JSON).content(objectMapper.writeValueAsString(request)));
//then
perform.andDo(print()).andExpect(status().isCreated())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$.name", equalTo(member.getName())))
.andExpect(jsonPath("$.nickname", equalTo(member.getNickname())))
.andExpect(jsonPath("$.id", equalTo(member.getId())))
.andExpect(jsonPath("$.role", equalTo(ROLE_MEMBER)));
verify(memberService, times(1)).createMember(any());
}
}
@DisplayName("회원 등록시 사용자로부터 받은 데이터가 검증 조건에 맞지 않은 경우 요청에 실패하도록 하는 테스트")
@Test
@WithMockUser
void signUpMember() throws Exception {
//given
MemberCreateRequest request = new MemberCreateRequest();
Mockito.when(memberService.createMember(any())).thenReturn(createResponse);
//when
ResultActions perform = mockMvc.perform(post("/api/service/members/sign-up").with(csrf())
.contentType(MediaType.APPLICATION_JSON).content(objectMapper.writeValueAsString(request)));
//then
perform.andDo(print()).andExpect(status().isBadRequest());
verify(memberService, never()).createMember(any());
}
@WithMockUser
애노테이션을 메소드 상단에 붙여주어 해결했다.with(csrf())
csrf 토큰을 넘겨줄 수 있게 한다.