Skip to content

Commit 92101f8

Browse files
authored
test(be): 상점 도메인 테스트 코드 추가 (#298)
* test(be): 상점 도메인 서비스 테스트 코드 추가 * test(be): 상품 도메인 서비스 테스트 코드 추가 * fix(be): 상품 키워드 업데이트 후 ES 동기화 이벤트 채널명 오타 수정
1 parent 5ad4ab0 commit 92101f8

File tree

7 files changed

+1068
-579
lines changed

7 files changed

+1068
-579
lines changed

backend/src/main/java/com/deliveranything/domain/product/product/publisher/ProductEventPublisher.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,6 @@ public class ProductEventPublisher {
1515

1616
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
1717
public void handleProductKeywordsChangedEvent(ProductKeywordsChangedEvent event) {
18-
redisTemplate.convertAndSend("product-events", event);
18+
redisTemplate.convertAndSend("product-keywords-events", event);
1919
}
2020
}

backend/src/main/java/com/deliveranything/domain/product/stock/entity/Stock.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public class Stock extends BaseEntity {
2929
@Version
3030
private Integer version;
3131

32-
@Column(nullable = false, columnDefinition = "INT DEFAULT 0")
32+
@Column(name = "quantity", nullable = false, columnDefinition = "INT DEFAULT 0")
3333
private Integer totalQuantity;
3434

3535
@Column(nullable = false, columnDefinition = "INT DEFAULT 0")
Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
package com.deliveranything.domain.product.product.service;
2+
3+
import static org.assertj.core.api.Assertions.assertThat;
4+
import static org.junit.jupiter.api.Assertions.assertThrows;
5+
import static org.mockito.ArgumentMatchers.any;
6+
import static org.mockito.ArgumentMatchers.anyLong;
7+
import static org.mockito.Mockito.doNothing;
8+
import static org.mockito.Mockito.doThrow;
9+
import static org.mockito.Mockito.mock;
10+
import static org.mockito.Mockito.never;
11+
import static org.mockito.Mockito.verify;
12+
import static org.mockito.Mockito.when;
13+
14+
import com.deliveranything.domain.product.product.dto.ProductCreateRequest;
15+
import com.deliveranything.domain.product.product.dto.ProductDetailResponse;
16+
import com.deliveranything.domain.product.product.dto.ProductResponse;
17+
import com.deliveranything.domain.product.product.dto.ProductSearchRequest;
18+
import com.deliveranything.domain.product.product.dto.ProductUpdateRequest;
19+
import com.deliveranything.domain.product.product.entity.Product;
20+
import com.deliveranything.domain.product.product.repository.ProductRepository;
21+
import com.deliveranything.domain.store.store.entity.Store;
22+
import com.deliveranything.domain.store.store.service.StoreService;
23+
import com.deliveranything.global.exception.CustomException;
24+
import com.deliveranything.global.exception.ErrorCode;
25+
import java.lang.reflect.Field;
26+
import java.util.List;
27+
import org.junit.jupiter.api.DisplayName;
28+
import org.junit.jupiter.api.Test;
29+
import org.junit.jupiter.api.extension.ExtendWith;
30+
import org.mockito.InjectMocks;
31+
import org.mockito.Mock;
32+
import org.mockito.junit.jupiter.MockitoExtension;
33+
import org.springframework.data.domain.PageRequest;
34+
import org.springframework.data.domain.SliceImpl;
35+
36+
@ExtendWith(MockitoExtension.class)
37+
@DisplayName("ProductService 테스트")
38+
class ProductServiceTest {
39+
40+
@Mock
41+
private ProductRepository productRepository;
42+
43+
@Mock
44+
private StoreService storeService;
45+
46+
@Mock
47+
private KeywordGenerationService keywordGenerationService;
48+
49+
@InjectMocks
50+
private ProductService productService;
51+
52+
// Helper method to create a Store object with ID for testing
53+
private Store createTestStore(Long id, Long sellerProfileId, String name) {
54+
Store store = Store.builder()
55+
.sellerProfileId(sellerProfileId)
56+
.name(name)
57+
.storeCategory(null) // Not relevant for product tests
58+
.roadAddr("Test Road")
59+
.imageUrl("test.jpg")
60+
.location(null)
61+
.build();
62+
try {
63+
Field idField = store.getClass().getSuperclass().getDeclaredField("id");
64+
idField.setAccessible(true);
65+
idField.set(store, id);
66+
} catch (NoSuchFieldException | IllegalAccessException e) {
67+
throw new RuntimeException("Failed to set ID for Store", e);
68+
}
69+
return store;
70+
}
71+
72+
// Helper method to create a Product object with ID for testing
73+
private Product createTestProduct(Long id, Store store, String name, String description, Integer price, String imageUrl, Integer initialStock) {
74+
Product product = Product.builder()
75+
.store(store)
76+
.name(name)
77+
.description(description)
78+
.price(price)
79+
.imageUrl(imageUrl)
80+
.initialStock(initialStock)
81+
.build();
82+
try {
83+
Field idField = product.getClass().getSuperclass().getDeclaredField("id");
84+
idField.setAccessible(true);
85+
idField.set(product, id);
86+
} catch (NoSuchFieldException | IllegalAccessException e) {
87+
throw new RuntimeException("Failed to set ID for Product", e);
88+
}
89+
return product;
90+
}
91+
92+
@Test
93+
@DisplayName("상품 생성 성공")
94+
void createProduct_success() {
95+
// given
96+
Long storeId = 1L;
97+
ProductCreateRequest request = new ProductCreateRequest("Test Product", "Description", 1000, "product.jpg", 10);
98+
Store store = createTestStore(storeId, 1L, "Test Store");
99+
100+
Product savedProductMock = mock(Product.class);
101+
when(savedProductMock.getId()).thenReturn(1L);
102+
when(savedProductMock.getName()).thenReturn(request.name());
103+
when(savedProductMock.getStore()).thenReturn(store); // Mock the store for ProductResponse.from
104+
105+
when(storeService.getStoreById(storeId)).thenReturn(store);
106+
when(productRepository.save(any(Product.class))).thenReturn(savedProductMock);
107+
doNothing().when(keywordGenerationService).generateAndSaveKeywords(anyLong());
108+
109+
// when
110+
ProductResponse response = productService.createProduct(storeId, request);
111+
112+
// then
113+
assertThat(response.productId()).isEqualTo(1L);
114+
assertThat(response.name()).isEqualTo("Test Product");
115+
verify(storeService).getStoreById(storeId);
116+
verify(productRepository).save(any(Product.class));
117+
verify(keywordGenerationService).generateAndSaveKeywords(1L);
118+
}
119+
120+
@Test
121+
@DisplayName("상품 삭제 성공")
122+
void deleteProduct_success() {
123+
// given
124+
Long storeId = 1L;
125+
Long productId = 1L;
126+
Store store = createTestStore(storeId, 1L, "Test Store");
127+
Product product = mock(Product.class); // Make product a mock
128+
// when(product.getId()).thenReturn(productId); // Not directly used by deleteProduct or validateStore
129+
130+
when(productRepository.getById(productId)).thenReturn(product);
131+
doNothing().when(productRepository).delete(any(Product.class));
132+
133+
// when
134+
productService.deleteProduct(storeId, productId);
135+
136+
// then
137+
verify(productRepository).getById(productId);
138+
verify(product).validateStore(storeId); // Verify interaction with the mock product
139+
verify(productRepository).delete(product);
140+
}
141+
142+
@Test
143+
@DisplayName("상품 삭제 실패 - 상점 ID 불일치")
144+
void deleteProduct_fail_storeIdMismatch() {
145+
// given
146+
Long storeId = 1L;
147+
Long productId = 1L;
148+
Long wrongStoreId = 2L;
149+
Store store = createTestStore(storeId, 1L, "Test Store");
150+
Product product = mock(Product.class); // Make product a mock
151+
// when(product.getId()).thenReturn(productId); // Not directly used by deleteProduct or validateStore
152+
153+
when(productRepository.getById(productId)).thenReturn(product);
154+
doThrow(new CustomException(ErrorCode.PRODUCT_STORE_MISMATCH))
155+
.when(product).validateStore(wrongStoreId);
156+
157+
// when & then
158+
CustomException exception = assertThrows(CustomException.class, () ->
159+
productService.deleteProduct(wrongStoreId, productId));
160+
assertThat(exception.getCode()).isEqualTo(ErrorCode.PRODUCT_STORE_MISMATCH.getCode());
161+
verify(productRepository).getById(productId);
162+
verify(productRepository, never()).delete(any(Product.class));
163+
verify(product).validateStore(wrongStoreId); // Verify interaction with the mock product
164+
}
165+
166+
@Test
167+
@DisplayName("상품 업데이트 성공")
168+
void updateProduct_success() {
169+
// given
170+
Long storeId = 1L;
171+
Long productId = 1L;
172+
ProductUpdateRequest request = new ProductUpdateRequest("Updated Product", "Updated Desc", 2000, 20, "updated.jpg");
173+
Store store = createTestStore(storeId, 1L, "Test Store");
174+
Product existingProduct = createTestProduct(productId, store, "Original Product", "Original Desc", 1000, "original.jpg", 10);
175+
176+
when(productRepository.getById(productId)).thenReturn(existingProduct);
177+
doNothing().when(keywordGenerationService).generateAndSaveKeywords(anyLong());
178+
179+
// when
180+
ProductResponse response = productService.updateProduct(storeId, productId, request);
181+
182+
// then
183+
assertThat(response.name()).isEqualTo("Updated Product");
184+
assertThat(response.price()).isEqualTo(2000);
185+
assertThat(response.imageUrl()).isEqualTo("updated.jpg");
186+
assertThat(existingProduct.getStock().getTotalQuantity()).isEqualTo(20);
187+
verify(productRepository).getById(productId);
188+
verify(keywordGenerationService).generateAndSaveKeywords(productId);
189+
}
190+
191+
@Test
192+
@DisplayName("상품 조회 성공")
193+
void getProduct_success() {
194+
// given
195+
Long storeId = 1L;
196+
Long productId = 1L;
197+
Store store = createTestStore(storeId, 1L, "Test Store");
198+
Product product = createTestProduct(productId, store, "Test Product", "Description", 1000, "product.jpg", 10);
199+
200+
when(productRepository.getById(productId)).thenReturn(product);
201+
202+
// when
203+
ProductDetailResponse response = productService.getProduct(storeId, productId);
204+
205+
// then
206+
assertThat(response.productId()).isEqualTo(productId);
207+
assertThat(response.name()).isEqualTo("Test Product");
208+
verify(productRepository).getById(productId);
209+
}
210+
211+
@Test
212+
@DisplayName("상품 검색 성공")
213+
void searchProducts_success() {
214+
// given
215+
Long storeId = 1L;
216+
ProductSearchRequest request = new ProductSearchRequest("keyword", null, 10);
217+
Store store = createTestStore(storeId, 1L, "Test Store");
218+
Product product = createTestProduct(1L, store, "Found Product", "Description", 1000, "product.jpg", 10);
219+
SliceImpl<Product> productSlice = new SliceImpl<>(List.of(product), PageRequest.of(0, 10), false);
220+
221+
when(storeService.getStoreById(storeId)).thenReturn(store);
222+
when(productRepository.search(storeId, request)).thenReturn(productSlice);
223+
224+
// when
225+
var responseSlice = productService.searchProducts(storeId, request);
226+
227+
// then
228+
assertThat(responseSlice.getContent()).hasSize(1);
229+
assertThat(responseSlice.getContent().get(0).name()).isEqualTo("Found Product");
230+
verify(storeService).getStoreById(storeId);
231+
verify(productRepository).search(storeId, request);
232+
}
233+
234+
@Test
235+
@DisplayName("ID로 상품 조회 성공")
236+
void getProductById_success() {
237+
// given
238+
Long productId = 1L;
239+
Store store = createTestStore(1L, 1L, "Test Store");
240+
Product product = createTestProduct(productId, store, "Test Product", "Description", 1000, "product.jpg", 10);
241+
242+
when(productRepository.getById(productId)).thenReturn(product);
243+
244+
// when
245+
Product foundProduct = productService.getProductById(productId);
246+
247+
// then
248+
assertThat(foundProduct.getId()).isEqualTo(productId);
249+
assertThat(foundProduct.getName()).isEqualTo("Test Product");
250+
verify(productRepository).getById(productId);
251+
}
252+
}

0 commit comments

Comments
 (0)