원인 발생

MongoDB와 Cache를 활용해서 카트를 저장하고 조회하는 서비스를 만들었고 이후 이에대한 테스트를 작성하는 과정에서 문제가 발생하였다.

서비스 코드

@CachePut(cacheNames = "userCart", key = "args[1]")  
public CartResponseDto createCart(Long productId, String userId) {  
	// 일반적인 Product 생성
    Product product = productRepository.findById(productId)  
        .orElseThrow(() -> new ProductException(  
            ProductErrorCode.PRODUCT_NOT_FOUND)  
        );  
  
    Query query = new Query();  
    query.addCriteria(where("userId").is(userId));  
  
    Update update = new Update();  
    update.set("storeId", product.getStore().getId());  
    update.set("productDto", ProductDto.createProduct(product));  
    
    mongoTemplate.upsert(query, update, Cart.class);  
  
    Cart cart = mongoTemplate.query(Cart.class)  
        .matching(query(where("userId").is(userId))).firstValue();  
  
    return CartResponseDto.from(cart);  
}

CachePut어노테이션을 사용해서 메소드의 Output을 userId를 기준으로 캐시를 진행하도록 작성하였다. 내부적으로는 넘겨받은 userId 파라미터를 기준으로 데이터가 있으면 update 없으면 insert하도록 진행한다.

테스트코드

@DisplayName("카트 생성")  
@Test  
public void 카트_생성() {  
	// given  
	String name = "name";  
	Store store = mock(Store.class);  
	Long productId = 1L;  
	Product product = Product.of(store, name, 1200.0, "description");  
	String userId = "userOne";  
 
	// when  
	when(productRepository.findById(anyLong()))
	.thenReturn(Optional.of(product));  
 
	// then  
	CartResponseDto cartResponseDto 
		= cartService.createCart(productId, userId);  
 
	...
 
}  
 
@DisplayName("캐시 적용 유무 확인")  
@Test  
public void 캐시_적용확인() {  
	// given  
	String name = "name";  
	String userId = "userId";  
	Long productId = 1L;  
	Store store = mock(Store.class);  
	Product product = Product.of(store, name, 1200.0, "description");  
 
	// productDB는 mocking한다.          
	when(productRepository.findById(anyLong()))
	.thenReturn(Optional.of(product));  
 
	// 카트 생성  
	cartService.createCart(productId, userId);  
 
	// then        
	// find에서는 호출이 안되므로 create 할 때만 호출되어야 한다.  
	verify(mongoTemplate, times(1)).query(Cart.class);  
 
}

두 테스트 모두 동일하게 createCart를 실행을 한다.

verify(mongoTemplate, times(1)).query(Cart.class);
여기서 아래 캐시 적용확인 메서드만을 호출하는 경우 위의 호출 횟수가 2번 이지만 두 테스트를 동시에 실행하면 위의 호출 횟수가 1번으로 나온다.