πŸ’» Knowledge/ν…ŒμŠ€νŠΈμ½”λ“œ

더 λ‚˜μ€ ν…ŒμŠ€νŠΈλ₯Ό μž‘μ„±ν•˜κΈ° μœ„ν•œ ꡬ체적 μ‘°μ–Έ + λ‚˜μ˜ 생각

ming412 2024. 12. 31. 15:50

ν…ŒμŠ€νŠΈ ν™˜κ²½μ˜ 독립성을 보μž₯ν•˜μž

μ•„λž˜μ™€ 같은 ν…ŒμŠ€νŠΈ μ½”λ“œκ°€ μžˆλ‹€κ³  μƒκ°ν•˜μž.

@DisplayName("μž¬κ³ κ°€ λΆ€μ‘±ν•œ μƒν’ˆμœΌλ‘œ 주문을 μƒμ„±ν•˜λ €λŠ” 경우 μ˜ˆμ™Έκ°€ λ°œμƒν•œλ‹€.")
@Test
void createOrderWithNoStock() {
    // given
    LocalDateTime registeredDateTime = LocalDateTime.now();

    Product product1 = createProduct(BOTTLE, "001", 1000);
    Product product2 = createProduct(BAKERY, "002", 3000);
    Product product3 = createProduct(HANDMADE, "003", 5000);
    productRepository.saveAll(List.of(product1, product2, product3);

    Stock stock1 = Stock.create("001", 2);
    Stock stock2 = Stock.create("002", 2);
    stock1.deductQuantity(1); // μˆ˜λŸ‰μ„ κ°μ†Œμ‹œν‚€λŠ” λ©”μ„œλ“œ
    stockRepository.saveAll(List.of(stock1, stock2));

    OrderCreateServiceRequest request = OrderCreateServiceRequest.builder()
        .productNumbers(List.of("001", "001", "002", "003")
        .build();

    // when // then
    assertThatThrownBy(() -> orderService.createOrder(reqeust, requestDateTime))
        .isInstanceOf(IllegalArgumentException.class)
        .hasMessage("μž¬κ³ κ°€ λΆ€μ‘±ν•œ μƒν’ˆμ΄ μžˆμŠ΅λ‹ˆλ‹€.");

}

 

μœ„ ν…ŒμŠ€νŠΈ μ½”λ“œμ—μ„œ ν™•μΈν•˜κ³  싢은 것은 μž¬κ³ κ°€ λΆ€μ‘±ν•œ μƒν’ˆμœΌλ‘œ 주문을 생성할 λ•Œ μ˜¬λ°”λ₯Έ μ˜ˆμ™Έκ°€ λ°œμƒν•˜λŠ”μ§€μ΄λ‹€.

 

stock1.deductQuantity(1); // μˆ˜λŸ‰μ„ κ°μ†Œμ‹œν‚€λŠ” λ©”μ„œλ“œ

 

λ¬Έμ œλŠ” μœ„ λ©”μ„œλ“œμΈλ°,

μœ„ λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•¨μœΌλ‘œμ¨ ν…ŒμŠ€νŠΈ ν™˜κ²½μ˜ 독립성을 보μž₯ν•  수 μ—†κ²Œ 된 것이닀.

 

ν˜„μž¬ ν…ŒμŠ€νŠΈ μ½”λ“œμ—μ„œ `stock1`의 재고 μˆ˜λŸ‰μ€ 2이닀.

 

μ΄λ•Œ `stock1.deductQuantity(1);`이 μ•„λ‹ˆλΌ `stock1.deductQuantity(3);`을 ν•œλ‹€λ©΄,  재고 μˆ˜λŸ‰μ΄ λΆ€μ‘±ν•˜μ—¬ "차감할 재고 μˆ˜λŸ‰μ΄ μ—†μŠ΅λ‹ˆλ‹€."λΌλŠ” μ˜ˆμ™Έ λ©”μ‹œμ§€λ‘œ ν…ŒμŠ€νŠΈκ°€ μ‹€νŒ¨ν•œλ‹€.

 

즉, 재고 차감 λ©”μ„œλ“œλ‘œ 인해 μ£Όλ¬Έ 생성 μ‹œ 재고 λΆ€μ‘± ν…ŒμŠ€νŠΈ ν™˜κ²½μ˜ 독립성이 보μž₯λ˜μ§€ μ•Šμ€ 것이닀.

 

ν•œ λˆˆμ— λ“€μ–΄μ˜€λŠ” Test Fixture κ΅¬μ„±ν•˜κΈ°

βœ… Fixture: κ³ μ •λ¬Ό, κ³ μ •λ˜μ–΄ μžˆλŠ” 물체

 

Test Fixtureλž€ ν…ŒμŠ€νŠΈλ₯Ό μœ„ν•΄ μ›ν•˜λŠ” μƒνƒœλ‘œ κ³ μ •μ‹œν‚¨ 객체둜, given μ ˆμ„ ꡬ성할 λ•Œ 주둜 λ§Œλ‚˜κ²Œ λœλ‹€. ν…ŒμŠ€νŠΈκ°€ μ‹œμž‘λ˜κΈ° 전에 ν•„μš”ν•œ 초기 μƒνƒœλ₯Ό μ„€μ •ν•˜κ±°λ‚˜ ν•„μš”ν•œ 데이터 λ˜λŠ” 객체λ₯Ό μ€€λΉ„ν•˜λŠ” 역할을 ν•œλ‹€.

 

κ²°λ‘ λΆ€ν„° λ§ν•˜μžλ©΄, Test Fixtureλ₯Ό νŒŒνŽΈν™”ν•˜λŠ” 것은 "λ¬Έμ„œ"λ‘œμ„œμ˜ ν…ŒμŠ€νŠΈλ₯Ό ꡬ성할 λ•Œ μ΄ν•΄ν•˜κΈ° μ–΄λ ΅κ²Œ λ§Œλ“ λ‹€.

 

ν…ŒμŠ€νŠΈλŠ” λ‹¨μˆœνžˆ μ½”λ“œμ˜ λ™μž‘μ„ κ²€μ¦ν•˜λŠ” 것을 λ„˜μ–΄, ν…ŒμŠ€νŠΈμ˜ μ˜λ„λ₯Ό λ¬Έμ„œμ²˜λŸΌ λͺ…ν™•νžˆ λ“œλŸ¬λ‚΄μ•Ό ν•œλ‹€. ν…ŒμŠ€νŠΈλ₯Ό μ½λŠ” μ‚¬λžŒμ΄ given절만 보고도 "이 ν…ŒμŠ€νŠΈκ°€ μ–΄λ–€ 상황을 μ„€μ •ν•˜κ³ μž ν•˜λŠ”μ§€"λ₯Ό μ‰½κ²Œ 이해할 수 μžˆμ–΄μ•Ό ν•œλ‹€. 

 

λ”°λΌμ„œ given절이 길어지더라도 ν…ŒμŠ€νŠΈκ°€ λ‹¨λ…μœΌλ‘œ λ™μž‘ν•˜λ„λ‘ κ΅¬μ„±ν•˜λŠ” 것이 λ°”λžŒμ§ν•˜λ‹€.

 

μΌλ‘€λ‘œ, λͺ‡ λ…„ μ „ μ‚¬μ΄λ“œ ν”„λ‘œμ νŠΈλ₯Ό μ§„ν–‰ν•  λ•Œμ—λŠ” μ•„λž˜μ²˜λŸΌ Test Fixtureλ₯Ό λ³„λ„μ˜ 클래슀둜 λΆ„λ¦¬ν•˜κΈ°λ„ ν–ˆμ—ˆλ‹€.

@Getter
@AllArgsConstructor
@RequiredArgsConstructor
public enum PetInfoFixture {

	VALID_PET("체리", 10, FOX, MALE, "hello! I'm 10 years Old, Wild fox!");

	private String name;
	private Integer age;
	private PetType type;
	private String bio;

	public PetInfo toEntity() {
		return PetInfo.builder()
			.name(name)
			.age(age)
			.type(type)
			.bio(bio)
			.build();
	}

	public PetProfileCreateRequest toRequest() {
		return PetProfileCreateRequest.builder()
			.name(name)
			.age(age)
			.type(type.toString())
			.bio(bio)
			.build();
	}
}
이 λ‹Ήμ‹œμ—λŠ” ν…ŒμŠ€νŠΈ μ½”λ“œμ— λŒ€ν•΄ 잘 λͺ¨λ₯΄λŠ” μƒνƒœμ˜€κΈ° λ•Œλ¬Έμ— Test Fixtureλ₯Ό λ”°λ‘œ λΆ„λ¦¬ν•˜λŠ” 것이 깔끔해 λ³΄μ˜€κ³ , μ—­ν• κ³Ό μ±…μž„μ„ λͺ…ν™•νžˆ ν•˜λŠ” 것이라 μƒκ°ν–ˆμ—ˆλ‹€. ν•˜μ§€λ§Œ μ§€κΈˆ 생각해보면, 이런 방식은 μ§€λ‚˜μΉ˜κ²Œ μΌλ°˜ν™”λ˜μ–΄ 각 ν…ŒμŠ€νŠΈμ˜ λ§₯락을 λͺ…ν™•νžˆ λ“œλŸ¬λ‚΄λŠ” 데 μ†Œν™€ν–ˆλ˜ λ°©λ²•μ΄μ—ˆλ‹€κ³  μƒκ°λœλ‹€.