Test Code
✅ Test Code
반복적인 검증 과정을 줄이기 위한 코드 test 단워: 클래스의 단위가 이상적인 단위 또는 메서드 단위
- Unit Test: 코드 일부분 테스트
- Integration Test
- Acceptance Test
☑️ how to use
✔️ JAVA Test JUNIT Annotation @ Test @BeforeEach @DisplayName ✔️ AssertJ ✔️ Given - When - Then
✅ Pure Unit Test
외부 의존성이 없는 소스 코드 검증 외부 라이브러리 동작 검증
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//mapper
@Mapper
public interface ItemMapper {
ItemMapper INSTANCE = Mappers.getMapper(ItemMapper.class);
@Mapping(target = "spec.cpu", source = "cpu")
@Mapping(target= "spec.capacity", source= "capacity")
Item itemEntityToItem(ItemEntity itemEntity);
@Mapping(target= "cpu", source= "itemBody.spec.cpu")
@Mapping(target= "capacity", source= "itemBody.spec.capacity")
@Mapping(target= "store", ignore= true)
@Mapping(target= "stock", expression ="java(0)")
ItemEntity idAndItemBodyToItemEntity(Integer id, ItemBody itemBody);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//ItemMapperUtilTest
class ItemMapperUtilTest {
@DisplayName("ItemEntity에 있는 ItemEntityToItem method test")
@Test
void itemEntityToItem() {
//given
ItemEntity itemEntity = ItemEntity.builder()
.name("name")
.type("type")
.id(1)
.price(1000)
.stock(0)
.cpu("CPU 1")
.capacity("100G")
.storeSalesEntity(new StoreSalesEntity())
.build();
//when
Item item= ItemMapper.INSTANCE.itemEntityToItem(itemEntity);
//then
log.info("item created" + item);
assertEquals(itemEntity.getPrice(), item.getPrice());
assertEquals(itemEntity.getId().toString(), item.getId());
assertEquals(itemEntity.getCpu(), item.getSpec().getCpu());
assertEquals(itemEntity.getCapacity(), item.getSpec().getCapacity());
}
}
✅ Mocking Unit Test
서비스, 비스니스 로직 테스트 하나의 Mocking Unit Test에서 여러가지 경우를 한 번에 테스트할 수 있다.
❓ 그런데 서비스 로직에는 repository를 의존하고 있는데? 의존성 문제?
가짜(mock) repository를 넣어준다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
@Slf4j
class AirReservationServiceUnitTest {
//user Repository, airline Repository 필요
@Mock
private UserJpaRepository userJpaRepository;
@Mock
private AirlineTicketJpaRepository airlineTicketJpaRepository;
@InjectMocks //두 mock repository를 이 service에 넣어주세요
private AirReservationService airReservationService;
@BeforeEach
public void setUp(){
MockitoAnnotations.openMocks(this);
}
@DisplayName("airlineTicket에 해당하는 user. ticket 모두 존재")
@Test
void findUserFavoritePlaceTicketsCaseSuccess() {
//given
Integer userId= 5;
String likePlace= "파리";
String ticketType= "왕복";
UserEntity userEntity= UserEntity.builder() //test위해 하나 만들어서 넣어줘야 함
.userId(userId)
.likeTravelPlace(likePlace)
.userName("name1")
.phoneNum("1234")
.build();
List<AirlineTicket> airlineTickets = Arrays.asList( //test위해 하나 만들어서 넣어줘야 함
AirlineTicket.builder()
.ticketId(1)
.arrivalLocation(likePlace)
.ticketType(ticketType)
.build(),
AirlineTicket.builder()
.ticketId(2)
.arrivalLocation(likePlace)
.ticketType(ticketType)
.build(),
AirlineTicket.builder()
.ticketId(3)
.arrivalLocation(likePlace)
.ticketType(ticketType)
.build(),
AirlineTicket.builder()
.ticketId(4)
.arrivalLocation(likePlace)
.ticketType(ticketType)
.build()
);
//when
when(userJpaRepository.findById(any())).thenReturn(Optional.of(userEntity)); //어떤 값이 들어가든간에
when(airlineTicketJpaRepository.findAirlineTicketsByArrivalLocationAndTicketType(likePlace, ticketType))
.thenReturn(airlineTickets);
//then
List<Ticket> tickets= airReservationService.findUserFavoritePlaceTicket(userId, ticketType);
log.info("tickets" + tickets);
assertTrue(
tickets.stream().map(Ticket::getArrival).allMatch((arrival)->arrival.equals(likePlace))
);
}
@DisplayName("airlineTicket에 왕복/편도 ticketType 틀린 경우")
@Test
void findUserFavoritePlaceTicketsCaseWrongTicketType() {
//given
Integer userId= 5;
String likePlace= "파리";
String ticketType= "!@#$%^&*()";
UserEntity userEntity= UserEntity.builder() //test위해 하나 만들어서 넣어줘야 함
.userId(userId)
.likeTravelPlace(likePlace)
.userName("name1")
.phoneNum("1234")
.build();
List<AirlineTicket> airlineTickets = Arrays.asList( //test위해 하나 만들어서 넣어줘야 함
AirlineTicket.builder()
.ticketId(1)
.arrivalLocation(likePlace)
.ticketType(ticketType)
.build(),
AirlineTicket.builder()
.ticketId(2)
.arrivalLocation(likePlace)
.ticketType(ticketType)
.build(),
AirlineTicket.builder()
.ticketId(3)
.arrivalLocation(likePlace)
.ticketType(ticketType)
.build(),
AirlineTicket.builder()
.ticketId(4)
.arrivalLocation(likePlace)
.ticketType(ticketType)
.build()
);
//when
when(userJpaRepository.findById(any())).thenReturn(Optional.of(userEntity)); //어떤 값이 들어가든간에
when(airlineTicketJpaRepository.findAirlineTicketsByArrivalLocationAndTicketType(likePlace, ticketType))
.thenReturn(airlineTickets);
//then
assertThrows(InvalidValueExceptions.class, //error뜨도록
() -> airReservationService.findUserFavoritePlaceTicket(userId, ticketType));
}
@DisplayName("User를 찾을 수 없는 경우")
@Test
void findUserFavoritePlaceTicketsCaseUserNotFound() {
//given
Integer userId= 5;
String likePlace= "파리";
String ticketType= "왕복";
UserEntity userEntity= null;
List<AirlineTicket> airlineTickets = Arrays.asList( //test위해 하나 만들어서 넣어줘야 함
AirlineTicket.builder()
.ticketId(1)
.arrivalLocation(likePlace)
.ticketType(ticketType)
.build(),
AirlineTicket.builder()
.ticketId(2)
.arrivalLocation(likePlace)
.ticketType(ticketType)
.build(),
AirlineTicket.builder()
.ticketId(3)
.arrivalLocation(likePlace)
.ticketType(ticketType)
.build(),
AirlineTicket.builder()
.ticketId(4)
.arrivalLocation(likePlace)
.ticketType(ticketType)
.build()
);
//when
when(userJpaRepository.findById(any())).thenReturn(Optional.ofNullable(userEntity)); //어떤 값이 들어가든간에
when(airlineTicketJpaRepository.findAirlineTicketsByArrivalLocationAndTicketType(likePlace, ticketType))
.thenReturn(airlineTickets);
//then
assertThrows(NotFoundException.class, //error뜨도록
() -> airReservationService.findUserFavoritePlaceTicket(userId, ticketType));
}
}
✅ Integration Test: Slice Test
원하는 layer만 테스트 @DataJapTest 실제 해당 Layer에 속하는 Bean만 호출, 더 빠르게 실행 가능
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class ReservationJpaRepositorJpaTest {
@Autowired
private ReservationJpaRepository reservationJpaRepository; //생성자 안 쓰고 @Autpwired
//repository에 해당되는 bean만 호출 가능
@Autowired
private PassengerJpaRepository passengerJpaRepository;
@Autowired
private AirlineTicketJpaRepository airlineTicketJpaRepository;
@DisplayName("ReservationRepository로 항공편 가격과 수수료 가격 검색")
@Test
void findFlightPriceAndCharge() {
//Given
Integer userId = 10;
//When
List<FlightPriceAndCharge> flightPriceAndCharges= reservationJpaRepository.findFlightPriceAndCharge(userId);
//Then
log.info("result" + flightPriceAndCharges);
}
@DisplayName("Reservation 에약 진행")
@Test
void saveReservation(){
//Given
Integer userId=10;
Integer ticketId = 5;
Passenger passenger= passengerJpaRepository.findPassengerByUserUserId(userId).get();
AirlineTicket airlineTicket= airlineTicketJpaRepository.findById(5).get();
//When
Reservation reservation= new Reservation(passenger, airlineTicket);
Reservation res= reservationJpaRepository.save(reservation);
//Then
log.info("reservation" + res);
assertEquals(res.getPassenger(), passenger);
assertEquals(res.getAirlineTicket(), airlineTicket);
}
}
✅ Acceptance Test Mock MVC
postman안하고도 바로 테스트 가능
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class AirReservationControllerSpringTest {
@Autowired
private MockMvc mockMvc;
@DisplayName("Find Airline Ticket Success")
@Test
void findAirlineTickets() throws Exception {
//Given
Integer userId = 5;
String ticketType= "왕복";
//When & Then
//AcceptanceTest 일 때는 When & Then 합쳐서 진행
String content= mockMvc.perform(
get("/v1/api/air-reservation/tickets") //api요청
.param("user-Id", userId.toString())
.param("airline-ticket-type", ticketType)
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8);
log.info("결과: " + content);
}
}
This post is licensed under CC BY 4.0 by the author.