ObjectMother를 적용하자
- https://martinfowler.com/bliki/ObjectMother.html
- 테스트에 필요한 데이터를 만드는데 도움을 주는 유틸리티
AccountFactory와 StudyFactoryt를 만들고 테스트 픽처 메소드 옮기기
- 상속 보다는 위임을
커스텀 애노테이션으로 테스트 애노테이션 묶음 만들기
@Retention(RetentionPilicy.RUNTIME)
@Target(ElementType.TYPE)
@Transactional
@SpringBootTest
@AutoConfigureMockMvc
public @interface MockMvcTest {
}
Account 생성을 도와주는 AccountFactory 클래스 생성
package me.weekbelt.studyolle.modules.account;
@Component
@RequiredArgsConstructor
public class AccountFactory {
@Autowired AccountRepository accountRepository;
public Account createAccount(String nickname) {
Account account = new Account();
account.setNickname(nickname);
account.setEmail(nickname + "@email.com");
accountRepository.save(account);
return account;
}
}
Study 생성을 도와주는 StudyFactory 클래스 생성
package me.weekbelt.studyolle.modules.study;
@Component
@RequiredArgsConstructor
public class StudyFactory {
@Autowired StudyService studyService;
@Autowired StudyRepository studyRepository;
public Study createStudy(String path, Account manager) {
Study study = new Study();
study.setPath(path);
studyService.createNewStudy(study, manager);
return study;
}
}
테스트 코드 리팩토링
공통적인 어노테이션을 모아 놓은 @MockMvcTest를 만들어주고 기존에 있던 어노테이션을 삭제하고 모든 Controller테스트에 적용시켜 준다.
package me.weekbelt.studyolle.infra;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Transactional
@SpringBootTest
@AutoConfigureMockMvc
public @interface MockMvcTest {
}
package me.weekbelt.studyolle.modules.study;
@MockMvcTest
public class StudyControllerTest {
// ..........
@Autowired AccountFactory accountFactory;
@Autowired StudyFactory studyFactory;
// ..........
@Test
@WithAccount("joohyuk")
@DisplayName("스터디 가입")
void joinStudy() throws Exception {
Account weekbelt = accountFactory.createAccount("weekbelt"); // 위임
Study study = studyFactory.createStudy("test-study", weekbelt); // 위임
mockMvc.perform(get("/study/" + study.getPath() + "/join"))
.andExpect(status().is3xxRedirection())
.andExpect(redirectedUrl("/study/" + study.getPath() + "/members"));
Account joohyuk = accountRepository.findByNickname("joohyuk");
assertThat(study.getMembers().contains(joohyuk)).isTrue();
}
@Test
@WithAccount("joohyuk")
@DisplayName("스터디 탈퇴")
void leaveStudy() throws Exception {
Account weekbelt = accountFactory.createAccount("weekbelt"); // 위임
Study study = studyFactory.createStudy("test-study", weekbelt); // 위임
Account joohyuk = accountRepository.findByNickname("joohyuk");
studyService.addMember(study, joohyuk);
mockMvc.perform(get("/study/" + study.getPath() + "/leave"))
.andExpect(status().is3xxRedirection())
.andExpect(redirectedUrl("/study/" + study.getPath() + "/members"));
assertThat(study.getMembers().contains(joohyuk)).isFalse();
}
}
package me.weekbelt.studyolle.modules.event;
@MockMvcTest
class EventControllerTest extends StudyControllerTest {
@Autowired MockMvc mockMvc;
@Autowired StudyFactory studyFactory;
@Autowired AccountFactory accountFactory;
@Autowired EventService eventService;
@Autowired EnrollmentRepository enrollmentRepository;
@Autowired AccountRepository accountRepository;
@DisplayName("선착순 모임에 참가 신청 - 자동 수락")
@WithAccount("joohyuk")
@Test
public void newEnrollment_to_FCFS_event_accepted() throws Exception {
Account weekbelt = accountFactory.createAccount("weekbelt");
Study study = studyFactory.createStudy("test-study", weekbelt);
Event event = createEvent("test-event", EventType.FCFS, 2, study, weekbelt);
mockMvc.perform(post("/study/" + study.getPath() + "/events/" + event.getId() + "/enroll")
.with(csrf()))
.andExpect(status().is3xxRedirection())
.andExpect(redirectedUrl("/study/" + study.getPath() + "/events/" + event.getId()));
Account joohyuk = accountRepository.findByNickname("joohyuk");
isAccepted(joohyuk, event);
}
@DisplayName("선착순 모임에 참가 신청 - 대기중 (이미 인원이 꽉차서)")
@WithAccount("joohyuk")
@Test
public void newEnrollment_to_FCFS_event_not_accepted() throws Exception {
Account weekbelt = accountFactory.createAccount("weekbelt");
Study study = studyFactory.createStudy("test-study", weekbelt);
Event event = createEvent("test-event", EventType.FCFS, 2, study, weekbelt);
Account may = accountFactory.createAccount("may");
Account june = accountFactory.createAccount("june");
eventService.newEnrollment(event, may);
eventService.newEnrollment(event, june);
mockMvc.perform(post("/study/" + study.getPath() + "/events/" + event.getId() + "/enroll")
.with(csrf()))
.andExpect(status().is3xxRedirection())
.andExpect(redirectedUrl("/study/" + study.getPath() + "/events/" + event.getId()));
Account joohyuk = accountRepository.findByNickname("joohyuk");
isNotAccepted(joohyuk, event);
}
@DisplayName("선착순 모임에 참가 신청 - 취소")
@WithAccount("joohyuk")
@Test
public void cancelEnrollment_to_FCFS_event_not_accepted() throws Exception {
Account joohyuk = accountRepository.findByNickname("joohyuk");
Account weekbelt = accountFactory.createAccount("weekbelt");
Account may = accountFactory.createAccount("may");
Study study = studyFactory.createStudy("test-study", weekbelt);
Event event = createEvent("test-event", EventType.FCFS, 2, study, weekbelt);
eventService.newEnrollment(event, may);
eventService.newEnrollment(event, joohyuk);
eventService.newEnrollment(event, weekbelt);
isAccepted(may, event);
isAccepted(joohyuk, event);
isNotAccepted(weekbelt, event);
mockMvc.perform(post("/study/" + study.getPath() + "/events/" + event.getId() + "/disenroll")
.with(csrf()))
.andExpect(status().is3xxRedirection())
.andExpect(redirectedUrl("/study/" + study.getPath() + "/events/" + event.getId()));
isAccepted(may, event);
isAccepted(weekbelt, event);
assertThat(enrollmentRepository.findByEventAndAccount(event, joohyuk)).isNull();
}
@DisplayName("관리자 확인 모임에 참가 신청 - 대기중")
@WithAccount("joohyuk")
@Test
public void newEnrollment_to_CONFIRMATIVE_event_not_accepted() throws Exception {
Account weekbelt = accountFactory.createAccount("weekbelt");
Study study = studyFactory.createStudy("test-study", weekbelt);
Event event = createEvent("test-event", EventType.CONFIRMATIVE, 2, study, weekbelt);
mockMvc.perform(post("/study/" + study.getPath() + "/events/" + event.getId() + "/enroll")
.with(csrf()))
.andExpect(status().is3xxRedirection())
.andExpect(redirectedUrl("/study/" + study.getPath() + "/events/" + event.getId()));
Account joohyuk = accountRepository.findByNickname("joohyuk");
isNotAccepted(joohyuk, event);
}
private void isAccepted(Account joohyuk, Event event) {
assertThat(enrollmentRepository.findByEventAndAccount(event, joohyuk).isAccepted()).isTrue();
}
private void isNotAccepted(Account joohyuk, Event event) {
assertThat(enrollmentRepository.findByEventAndAccount(event, joohyuk).isAccepted()).isFalse();
}
private Event createEvent(String eventTitle, EventType eventType, int limit, Study study, Account account) {
Event event = new Event();
event.setEventType(eventType);
event.setLimitOfEnrollments(limit);
event.setTitle(eventTitle);
event.setCreatedDateTime(LocalDateTime.now());
event.setEndEnrollmentDateTime(LocalDateTime.now().plusDays(1));
event.setStartDateTime(LocalDateTime.now().plusDays(1).plusHours(5));
event.setEndDateTime(LocalDateTime.now().plusDays(1).plusHours(7));
return eventService.createEvent(event, study, account);
}
}
참고: https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-JPA-%EC%9B%B9%EC%95%B1#
'스프링과 JPA 기반 웹 어플리케이션 개발 > 6부 패키지와 테스트 정리' 카테고리의 다른 글
69. 패키지 구조 정리 (0) | 2020.05.06 |
---|