프로젝트를 진행하고 있는 팀내에서 코드리뷰를 진행하기로 해 코드리뷰를 진행하면서 개선된 사항에 대해 얘기를 해보고자 합니다. 그 중에서도 테스트 코드의 엔티티 생성에 관해서요 !
개발자라면 응당 작성해보았을 테스트 코드, 어떻게 작성하고 계신가요 ?
아래는 저희 팀내의 누군가가 작성한 Respository의 테스트 코드 일부분입니다. 어떤 코드인 것 같나요?
@BeforeEach
public void beforeEach() {
HashtagRecord hashtag1 = hashtag1();
HashtagRecord hashtag2 = hashtag2();
List<HashtagRecord> hashtagRecords1 = List.of(hashtag1, hashtag2);
Convenience hdmi = hdmi();
Convenience park = park();
List<Convenience> roomConveniences1 = List.of(hdmi);
List<Convenience> cafeConveniences1 = List.of(park);
Studycafe studycafe1 = studycafe1(hashtagRecords1, cafeConveniences1); // 예약 내역 O
Room room1 = room1(roomConveniences1);
Room room2 = room1(roomConveniences1);
Room room3 = room1(roomConveniences1);
studycafe1.addRoom(room1);
studycafe1.addRoom(room2);
studycafe1.addRoom(room3);
Notice notice1 = notices();
studycafe1.addNotice(notice1);
ReservationRecord reservationRecord1 = reservationRecord_13_15(room1);
room1.addReservationRecord(reservationRecord1);
ReservationRecord reservationRecord2 = reservationRecord_13_15(room2);
room2.addReservationRecord(reservationRecord2);
ReservationRecord reservationRecord3 = reservationRecord_13_15(room3);
room3.addReservationRecord(reservationRecord3);
HashtagRecord hashtag3 = hashtag3();
HashtagRecord hashtag4 = hashtag4();
List<HashtagRecord> hashtagRecords2 = List.of(hashtag3, hashtag4);
Studycafe studycafe2 = studycafe2(hashtagRecords2); // 예약 내역 O
Room room4 = room2();
Room room5 = room2();
Room room6 = room2();
studycafe2.addRoom(room4);
studycafe2.addRoom(room5);
studycafe2.addRoom(room6);
Notice notice2 = notices();
studycafe2.addNotice(notice2);
ReservationRecord reservationRecord4 = reservationRecord_09_11(room4);
room4.addReservationRecord(reservationRecord4);
ReservationRecord reservationRecord5 = reservationRecord_09_11(room5);
room5.addReservationRecord(reservationRecord5);
ReservationRecord reservationRecord6 = reservationRecord_09_11(room6);
room6.addReservationRecord(reservationRecord6);
Convenience beam = beam();
Convenience elevator = elevator();
List<Convenience> roomConveniences2 = List.of(beam);
List<Convenience> cafeConveniences2 = List.of(elevator);
Studycafe studycafe3 = studycafe3(cafeConveniences2); // 예약 내역 X
Room room7 = room1(roomConveniences2);
Room room8 = room1(roomConveniences2);
Room room9 = room1(roomConveniences2);
studycafe3.addRoom(room7);
studycafe3.addRoom(room8);
studycafe3.addRoom(room9);
Notice notice3 = notices();
studycafe3.addNotice(notice3);
Studycafe studycafe4 = studycafe4(); // 예약 내역 X
Room room10 = room2();
Room room11 = room2();
Room room12 = room2();
studycafe4.addRoom(room10);
studycafe4.addRoom(room11);
studycafe4.addRoom(room12);
Notice notice4 = notices();
studycafe4.addNotice(notice4);
em.persist(studycafe1);
em.persist(studycafe2);
em.persist(studycafe3);
em.persist(studycafe4);
em.flush();
em.clear();
}
아무래도 @BeforeEach
를 통해 미리 필요한 모든 데이터들을 생성해놓고, 상황별 테스트를 아래에서 하려고 한 것 같습니다. 그러면 위에서 나온 hashtag1()
과 같은 메서드는 어디서 나왔을까요 ?
테스트 코드를 아래로 쭉 내려보면 찾을 수 있었습니다.
private Studycafe studycafe1(List<HashtagRecord> hashtagRecords, List<Convenience> conveniences) {
Studycafe studycafe = Studycafe.builder()
.name("테스트1 스터디카페")
.address(address())
.accumReserveCount(40)
.totalGrade(1.2)
.duration(null)
.nearestStation(null)
.introduction("소개글")
.build();
List<NotificationInfo> notificationInfos = notificationInfos();
for (NotificationInfo notificationInfo : notificationInfos) {
studycafe.addNotificationInfo(notificationInfo);
}
List<OperationInfo> operationInfos = operationInfos();
for (OperationInfo operationInfo : operationInfos) {
studycafe.addOperationInfo(operationInfo);
}
for (HashtagRecord hashtagRecord : hashtagRecords) {
studycafe.addHashtagRecord(hashtagRecord);
}
for (Convenience convenience : conveniences) {
studycafe.addConvenience(convenience);
}
return studycafe;
}
private Studycafe studycafe2(List<HashtagRecord> hashtagRecords) {
Studycafe studycafe = Studycafe.builder()
.name("테스트2 스터디카페")
.address(address())
.accumReserveCount(30)
.totalGrade(2.4)
.duration(null)
.nearestStation(null)
.introduction("소개글")
.build();
List<NotificationInfo> notificationInfos = notificationInfos();
for (NotificationInfo notificationInfo : notificationInfos) {
studycafe.addNotificationInfo(notificationInfo);
}
List<OperationInfo> operationInfos = operationInfos();
for (OperationInfo operationInfo : operationInfos) {
studycafe.addOperationInfo(operationInfo);
}
for (HashtagRecord hashtagRecord : hashtagRecords) {
studycafe.addHashtagRecord(hashtagRecord);
}
return studycafe;
}
private Studycafe studycafe3(List<Convenience> conveniences) {
Studycafe studycafe = Studycafe.builder()
.name("테스트3 스터디카페")
.address(address())
.accumReserveCount(20)
.totalGrade(3.6)
.duration(null)
.nearestStation(null)
.introduction("소개글")
.build();
List<NotificationInfo> notificationInfos = notificationInfos();
for (NotificationInfo notificationInfo : notificationInfos) {
studycafe.addNotificationInfo(notificationInfo);
}
List<OperationInfo> operationInfos = operationInfos();
for (OperationInfo operationInfo : operationInfos) {
studycafe.addOperationInfo(operationInfo);
}
for (Convenience convenience : conveniences) {
studycafe.addConvenience(convenience);
}
return studycafe;
}
private Studycafe studycafe4() {
Studycafe studycafe = Studycafe.builder()
.name("테스트4 스터디카페")
.address(address())
.accumReserveCount(10)
.totalGrade(4.8)
.duration(null)
.nearestStation(null)
.introduction("소개글")
.build();
List<NotificationInfo> notificationInfos = notificationInfos();
for (NotificationInfo notificationInfo : notificationInfos) {
studycafe.addNotificationInfo(notificationInfo);
}
List<OperationInfo> operationInfos = operationInfos();
for (OperationInfo operationInfo : operationInfos) {
studycafe.addOperationInfo(operationInfo);
}
return studycafe;
}
private Address address() {
return Address.builder()
.basic("서울특별시 중구 필동로 1길30")
.detail("동국대학교 명진관")
.zipcode("04620")
.build();
}
private List<NotificationInfo> notificationInfos() {
return Collections.singletonList(
NotificationInfo.builder()
.detail("공지 사항")
.startDate(LocalDate.of(2023, 6,30))
.endDate(LocalDate.of(2024, 6, 30))
.build()
);
}
private List<OperationInfo> operationInfos() {
return List.of(
OperationInfo.builder()
.week(Week.MONDAY)
.startTime(LocalTime.of(9, 0))
.endTime(LocalTime.of(21, 0))
.allDay(false)
.closed(false)
.build(),
OperationInfo.builder()
.week(Week.TUESDAY)
.startTime(LocalTime.of(9, 0))
.endTime(LocalTime.of(21, 0))
.allDay(false)
.closed(false)
.build(),
OperationInfo.builder()
.week(Week.WEDNESDAY)
.startTime(LocalTime.of(9, 0))
.endTime(LocalTime.of(21, 0))
.allDay(false)
.closed(false)
.build(),
OperationInfo.builder()
.week(Week.THURSDAY)
.startTime(LocalTime.of(9, 0))
.endTime(LocalTime.of(21, 0))
.allDay(false)
.closed(false)
.build(),
OperationInfo.builder()
.week(Week.FRIDAY)
.startTime(LocalTime.of(9, 0))
.endTime(LocalTime.of(21, 0))
.allDay(false)
.closed(false)
.build(),
OperationInfo.builder()
.week(Week.SATURDAY)
.startTime(LocalTime.of(9, 0))
.endTime(LocalTime.of(21, 0))
.allDay(false)
.closed(true) // 2023-07-29 에는 조회 안되도록 반영
.build(),
OperationInfo.builder()
.week(Week.SUNDAY)
.startTime(LocalTime.of(9, 0))
.endTime(LocalTime.of(21, 0))
.allDay(false)
.closed(false)
.build(),
OperationInfo.builder()
.week(Week.HOLIDAY)
.startTime(LocalTime.of(9, 0))
.endTime(LocalTime.of(21, 0))
.allDay(false)
.closed(false)
.build()
);
}
private Room room1(List<Convenience> conveniences) {
Room room = Room.builder()
.standardHeadCount(4)
.minHeadCount(2)
.maxHeadCount(4)
.minUsingTime(1)
.price(3000)
.build();
for (Convenience convenience : conveniences) {
room.addConvenience(convenience);
}
return room;
}
private Room room2() {
return Room.builder()
.standardHeadCount(10)
.minHeadCount(8)
.maxHeadCount(10)
.minUsingTime(1)
.price(3000)
.build();
}
private ReservationRecord reservationRecord_13_15(Room room) {
return ReservationRecord.builder()
.room(room)
.date(LocalDate.of(2023, 7, 30))
.startTime(LocalTime.of(13, 0))
.endTime(LocalTime.of(15, 0))
.duration(2)
.headCount(5)
.status(ReservationStatus.CONFIRMED)
.build();
}
private ReservationRecord reservationRecord_09_11(Room room) {
return ReservationRecord.builder()
.room(room)
.date(LocalDate.of(2023, 7, 30))
.startTime(LocalTime.of(9, 0))
.endTime(LocalTime.of(11, 0))
.duration(2)
.headCount(5)
.status(ReservationStatus.CONFIRMED)
.build();
}
private Notice notices() {
return Notice.builder()
.detail("유의 사항")
.build();
}
private List<HashtagName> hashtagNames() {
List<HashtagName> hashtagNames = new ArrayList<>();
hashtagNames.add(HashtagName.STATION_AREA);
hashtagNames.add(HashtagName.CLEAN);
return hashtagNames;
}
private HashtagRecord hashtag1() {
return HashtagRecord.builder()
.name(HashtagName.STATION_AREA)
.count(20)
.build();
}
private HashtagRecord hashtag2() {
return HashtagRecord.builder()
.name(HashtagName.CLEAN)
.count(10)
.build();
}
private HashtagRecord hashtag3() {
return HashtagRecord.builder()
.name(HashtagName.COST_EFFECTIVE)
.count(20)
.build();
}
private HashtagRecord hashtag4() {
return HashtagRecord.builder()
.name(HashtagName.ACCESS)
.count(10)
.build();
}
private List<ConvenienceName> convenienceNames() {
List<ConvenienceName> convenienceNames = new ArrayList<>();
convenienceNames.add(ConvenienceName.HDMI);
convenienceNames.add(ConvenienceName.PARKING);
return convenienceNames;
}
private Convenience hdmi() {
return Convenience.builder()
.name(ConvenienceName.HDMI)
.price(0)
.build();
}
private Convenience park() {
return Convenience.builder()
.name(ConvenienceName.PARKING)
.price(0)
.build();
}
private Convenience beam() {
return Convenience.builder()
.name(ConvenienceName.BEAM)
.price(0)
.build();
}
private Convenience elevator() {
return Convenience.builder()
.name(ConvenienceName.ELEVATOR)
.price(0)
.build();
}
private Pageable pageable() {
return PageRequestConverter.of(0, 8);
}
이게 어떻게 된 일인걸까요 ㅜ ㅜ 모든 엔티티를 해당 테스트 클래스 내에서 작성하고, 그에대한 이름 또한 studycafe1
과 같은 이름으로 작명되어 있네요. 그렇다면 이렇게 테스트코드를 작성할 경우의 문제점은 무엇일까요 ? 대표적으로 보이는 두가지 문제점을 살펴봐요.
지금 엔티티를 생성하는 메서드의 작명을 보면 아래와 같습니다.
studycafe1
, studycafe2
...room1
, room2
, ...hashtag1
, hashtag2
, ...hdmi
, elevator
, ...위의 메서드 명들만 보고도 어떤 특징을 가진 데이터를 생성하는 메서드 인지 알 수 없는게 가장 큰 문제점으로 보이는데요,, 작성하신 테스트 코드를 보면 아래과 같습니다.
` @Test
@DisplayName("예약 많은 순 정렬")
public void 예약_많은_순_정렬() throws Exception {
// given
SearchRequest request = SearchRequest.builder()
.sortType(SortType.RESERVATION_DESC)
.build();
// when
List<SearchResponse> responses = studycafeDslRepository.searchAll(request, pageable()).getContent();
// then
Assertions.assertThat(responses.get(0).getAccumRevCnt()).isEqualTo(40);
Assertions.assertThat(responses.get(1).getAccumRevCnt()).isEqualTo(30);
Assertions.assertThat(responses.get(2).getAccumRevCnt()).isEqualTo(20);
Assertions.assertThat(responses.get(3).getAccumRevCnt()).isEqualTo(10);
}
예약이 많은 순서대로 studycafe가 정렬이 되는지 테스트해 보는 코드로 보입니다.
그런데 Assertions
구문에서 쿼리를 통해 나온 결과물의 누적 예약 횟수가 각각 40, 30, 20, 10
과 같다고 작성되어 있네요. 그렇다면 팀원들은 이 코드를 보고 어떻게 테스트의 결과 값이 40, 30, 20, 10
이라는 것을 알 수 있을까요?
beforeEach
로 이동해서 지금 어떤 스터디카페들이 저장되고 있는지 파악합니다.studycafe1
, studycafe2
, studycafe3
, studycafe4
가 저장되고 있는 걸 볼 수 있습니다.studycafe1()
, studycafe2()
, studycafe3()
, studycafe4()
메서드를 살펴보면 각각의 accumReserveCount
를 확인할 수 있습니다.아.. 테스트 코드 하나에 너무 많은 시간이 드는 것 같아요ㅜㅜ
혼자 하는 개발이라면 상관없지만, 협업으로 진행되는 프로젝트이고, 팀원들의 코드리뷰가 이루어지는 상황이라면 많이 힘들 것 같아 보입니다.
위에서 @BeforeEach
를 통해서 모든 데이터들을 저장해놓고 테스트를 수행한다는 걸 확인했습니다. 그렇다면, 필터링 조건이 추가된다면 어떻게 될까요?
이런 필터가 추가될 일은 없겠지만,, 스터디카페에 방이 많은 개수대로 정렬을 하고 싶다면 어떻게 변경될까요 ?
위에서 코드를 다시 살펴보면 각각의 스터디카페에 방이 3개씩 일정하게 들어가는 걸 볼 수 있습니다.
Room room1 = room1(roomConveniences1);
Room room2 = room1(roomConveniences1);
Room room3 = room1(roomConveniences1);
studycafe1.addRoom(room1);
studycafe1.addRoom(room2);
studycafe1.addRoom(room3);
바로 위와 같이 말이죠! 하지만 새로 추가된 정렬 조건을 테스트해보려면 각각의 스터디 카페가 가지고 있는 room의 개수가 달라야겠죠 ?
그렇다면 이런 상황을 만들기 위해 기존에 추가 했던 room을 삭제 하거나 더 추가하게 될 겁니다. 그럼 전체적으로 모든 테스트를 수행할 수 있도록 짜놓은 beforEach()
가 틀어져 특정 room을 반환하는 테스트가 있을 경우 모두 수정해야 되는 상황이 벌어집니다.
정말 만약에 이러한 변경사항이 없고 진짜 완전히 최종 요구사항이다 ! 하는 경우에도 문제는 있을 것 같아요. beforeEach()
메서드를 작성할 때 모든 테스트가 수행될 수 있도록 데이터들을 설계해서 넣어야 하는데 이게 굉장히 머리가 아플 것 같기 때문입니다 😢
그렇다면 이러한 문제를 어떻게 해결해야 좋을까요? 변경에 취약하지 않으면서도 테스트코드만 봤을 때도 팀원들이 아! 어떤 테스트를 진행하고 있고 이 테스트는 정당하구나 라는걸 파악할 수 있는 테스트는 어떻게 작성 돼야 할까요 ?
위의 코드들을 보면 재사용성이 많은 코드들이 보이는 것 같습니다. 각각의 객체를 생성할 때 안에 넣는 데이터 값, 즉 필드의 값만 다를 뿐이지 모든 형태들이 똑같은 것을 확인할 수 있습니다.
그렇다면 이것들을 한곳에 모아서 재사용할 수 있게 만들어보는게 어떨까 ? 하는 생각이 들게 됩니다.
저는 평소에 다른 사람의 코드를 분석하는 것을 좋아합니다. 다른 사람의 코드를 이해해보면서 새로운 툴이나 패턴, 설계등을 깨닫게 되는게 좋은 것 같아요. 그래서 제가 옛날에 보았던 우테코 프로젝트 f12 에서 사용된 Enum Fixture를 적용해보고자 합니다.
먼저 간단한 예시를 들어보면 아래와 같은 Member
라는 엔티티가 있습니다.
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String email;
private String password;
private String nickname;
}
우리는 테스트 코드를 작성할 때 아래와 같은 상황에서 해당 엔티티의 객체를 사용해야한다고 생각해볼게요.
축소해서 4가지이지만, 사실 서비스를 개발하다보면 member
라는 엔티티는 많은 동작에서 필요한 객체입니다. 따라서 정말 많은 테스트에서 사용됩니다.
그렇다면 모든 테스트 코드 내에서 우리는 아래와 같이 생성할 수 있습니다.
Member member = new Member(null, "ddukddaki@naver.com", "ddukddaki12", "ddukddaki");
생성자를 통해 생성하거나 아래와 같이 빌더를 사용할 수도 있습니다.
Member member = Member.builder()
.id(null)
.email("ddukddaki@naver.com")
.password("ddukddaki12")
.nickname("ddukddaki")
.build();
생성자를 쓰는 패턴은 원하는 필드만 생성하지 못해 모든 필드데이터가 다 필요하지 않는 테스트 코드에서는 불필요하게 긴 코드가 됩니다. 그래서 보통 원하는 필드만 골라 넣어 생성할 수 있는 빌더 패턴을 많이 사용하곤합니다.
하지만 member를 여러명 생성해야 하는 경우 어떤가요?
Member member1 = Member.builder()
.id(null)
.email("ddukddaki1@naver.com")
.password("ddukddaki112")
.nickname("ddukddaki1")
.build();
Member member2 = Member.builder()
.id(null)
.email("ddukddaki2@naver.com")
.password("ddukddaki212")
.nickname("ddukddaki2")
.build();
코드가 굉장히 길어지는 걸 볼 수 있고, 같은 테스트 코드내에서 회원을 생성하기 때문에 같은 형태의 회원을 생성합니다. 따라서 빌더에 들어가는 형태가 같아 코드가 중복되는 것 처럼 보입니다. 그렇다면? 어떤식으로 이 중복을 해소할 수 있을까요 ?
public enum MemberFixture{
뚝딱이("ddukddaki1@naver.com", "ddukddaki112", "ddukddaki112")
private final String email;
private final String password;
private final String nickname;
public MemberFixture(String email, String password, String nickname){
this.email = email;
this.password = password;
this.nickname = nickname;
}
public Member 기본_생성(){
return Member.builder()
.id(null)
.email(this.email)
.password(this.password)
.nickname(this.nickname)
.build();
}
}
위와 같이 enum 클래스를 생성하면 아래와 같이 생성해서 사용할 수 있습니다.
Member member1 = 뚝딱이.기본_생성();
Member member2 = 뚝딱이.기본_생성();
member의 필드 값이 같아도 되는 경우 위와 같은 식으로 생성할 수 있습니다.
그렇다면 만약 db에 NOT NULL 로 저장돼야 하는 필드가 추가 되었다면 어떻게 될까요 ?
원래라면 member1(), member2, ..
메서드에서 필드들을 추가해야했지만 우리는 이제 enum이 있습니다! birthday
라는 필드가 추가된다고 생각해볼게요.
public enum MemberFixture{
뚝딱이("ddukddaki1@naver.com", "ddukddaki112", "ddukddaki112",LocalDate.of(2000,10,4));
private final String email;
private final String password;
private final String nickname;
private final LocalDate birthday;
public MemberFixture(String email, String password, String nickname, LocalDate birthday){
this.email = email;
this.password = password;
this.nickname = nickname;
this.birthday = birthday;
}
public Member 기본_생성(){
return Member.builder()
.id(null)
.email(this.email)
.password(this.password)
.nickname(this.nickname)
.birthday(this.birthday)
.build();
}
위와 같이 enum 클래스에 birthday
필드만 추가해주면 됩니다 ! 그럼 변동없이 코드를 사용할 수 있어요 아래처럼요 !
Member member1 = 뚝딱이.기본_생성();
Member member2 = 뚝딱이.기본_생성();
음.. 그런데 만약 회원이 생일날짜 별로 정렬이 되는지 테스트하는 코드를 짜려면 어떻게 해야할까요 ?
지금은 두 회원 모두 날짜가 고정적으로 심어져 있어서 날짜를 가늠하기 어려운 것 같아요. 날짜를 동적으로 받아 테스트에 활용할 수 없을까요 ?
public enum MemberFixture{
뚝딱이("ddukddaki1@naver.com", "ddukddaki112", "ddukddaki112");
private final String email;
private final String password;
private final String nickname;
public MemberFixture(String email, String password, String nickname){
this.email = email;
this.password = password;
this.nickname = nickname;
this.birthday = birthday;
}
public MemberBuilder 기본_생성_빌더(){
return Member.builder()
.id(null)
.email(this.email)
.password(this.password)
.nickname(this.nickname);
public Member 날짜_추가_생성(LocalDate birthday){
return 기본_생성_빌더()
.birthday(birthday)
.build();
}
}
위와 같이 고정되어 있던 필드를 삭제하고 메서드를 추가해 날짜를 입력 받아 회원을 생성할 수 있게 할 수 있어요 ! 그럼 아래와 같이 생일이 다른 회원을 생성할 수 있어요!
Member member1 = 뚝딱이.날짜_추가_생성(LocalDate.of(2000, 10, 4));
Member member2 = 뚝딱이.날짜_추가_생성(LocalDate.of(2002, 12, 18));
그렇다면 마지막으로 member의 권한을 상징하는 필드가 생겨서 권한 별로 테스트를 진행하고 싶어요. 그럼 어떻게 해야할까요? 권한이 사장님
과 손님
두가지일 때 우린 어떻게 작성해야할 까요 ? 위에서 birthday를 동적으로 받기 위해 메서드를 추가한 것 처럼 role을 입력 받는 메서드를 추가해 동적으로 입력을 받아야하는 걸까요?
위의 방법대로 해도 괜찮지만 role처럼 가능한 범위가 적을 때는 fixture를 하나 더 늘리는 것도 방법이 될 수 있어요.
public enum MemberFixture{
사장님("ddukddaki1@naver.com", "ddukddaki112", "ddukddaki112", "사장님"),
손님("customer@naver.com", "customer12", "customer", "손님");
private final String email;
private final String password;
private final String nickname;
private final String role;
public MemberFixture(String email, String password, String nickname, String role){
this.email = email;
this.password = password;
this.nickname = nickname;
this.birthday = birthday;
this.role = role;
}
public MemberBuilder 기본_생성_빌더(){
return Member.builder()
.id(null)
.email(this.email)
.password(this.password)
.nickname(this.nickname)
.role(this.role);
public Member 기본_생성(){
return 기본_생성_빌더().build();
public Member 날짜_추가_생성(LocalDate birthday){
return 기본_생성_빌더()
.birthday(birthday)
.build();
}
}
위와 같이 사장님과 손님 fixture를 만들어서 사용할 수 있어요
Member owner = 사장님.기본_생성();
Member customer = 손님.기본_생성();
따라서 위와 같이 간단하게 사장님 권한을 가진 owner
와 손님 권한을 가진 customer
를 생성할 수 있습니다.
주의할 점이 있는데요, 픽스처를 여러개 만들 경우 입니다.
위에서 우린 studycafe1(), studycafe2(),..
를 통한 작명의 문제점을 봤어요. 그렇다면 픽스처를 작명을 할 때도 이러한 문제점을 생각하며 작명해야합니다.
위에서 권한별로 픽스처의 이름이 사장님
, 손님
으로 된 것 처럼 픽스처를 여러개 생성할 때는 각각의 픽스처의 특징이 잘 나타나도록 작명해야해요.
또한 동적으로 들어갈 데이터와 정적으로 들어갈 데이터를 잘 구분할 수 있어야합니다.
픽스처는 혼자만 사용하는게 아닌 팀원 모두가 사용한다는 것을 명심해야합니다. 해당 픽스처가 사용될 수 있는 테스트를 생각하고 이에 맞춰서 설계하고 메서드를 추가하는 것이 중요할 것 같습니다.
마지막으로 테스트코드와 관련된 글들을 읽다가 좋은 테스트에 대해 작성해주신 글귀가 있어 가져왔습니다.
좋은 테스트 코드는 읽는 사람 입장에서 이 테스트를 이해하는데 필요한 모든 정보를, 테스트 케이스 본문에 담고 있는 테스트를 말합니다.
https://github.com/woowacourse-teams/2022-f12
https://tech.inflab.com/20230404-test-code/#두번째-테스트는-구현이-아닌-결과를-검증하도록-한다
https://jaehoney.tistory.com/274
좋은 글 잘 읽었습니다!!