스프링/6. 스프링 DB-2

15. @Transactional로 테스트

sdafdq 2023. 10. 9. 01:31
@Transactional
@SpringBootTest
class ItemRepositoryTest {

    @Autowired
    ItemRepository itemRepository;

    @AfterEach
    void afterEach() {
        //MemoryItemRepository 의 경우 제한적으로 사용
        if (itemRepository instanceof MemoryItemRepository) {
            ((MemoryItemRepository) itemRepository).clearStore();
        }
    }

    @Test
    void save() {
        //given
        Item item = new Item("itemA", 10000, 10);

        //when
        Item savedItem = itemRepository.save(item);

        //then
        Item findItem = itemRepository.findById(item.getId()).get();
        assertThat(findItem).isEqualTo(savedItem);
    }

    @Test
    void updateItem() {
        //given
        Item item = new Item("item1", 10000, 10);
        Item savedItem = itemRepository.save(item);
        Long itemId = savedItem.getId();

        //when
        ItemUpdateDto updateParam = new ItemUpdateDto("item2", 20000, 30);
        itemRepository.update(itemId, updateParam);

        //then
        Item findItem = itemRepository.findById(itemId).get();
        assertThat(findItem.getItemName()).isEqualTo(updateParam.getItemName());
        assertThat(findItem.getPrice()).isEqualTo(updateParam.getPrice());
        assertThat(findItem.getQuantity()).isEqualTo(updateParam.getQuantity());
    }

    @Test
    void findItems() {
        //given
        Item item1 = new Item("itemA-1", 10000, 10);
        Item item2 = new Item("itemA-2", 20000, 20);
        Item item3 = new Item("itemB-1", 30000, 30);

        itemRepository.save(item1);
        itemRepository.save(item2);
        itemRepository.save(item3);

        //둘 다 없음 검증
        test(null, null, item1, item2, item3);
        test("", null, item1, item2, item3);

        //itemName 검증
        test("itemA", null, item1, item2);
        test("temA", null, item1, item2);
        test("itemB", null, item3);

        //maxPrice 검증
        test(null, 10000, item1);

        //둘 다 있음 검증
        test("itemA", 10000, item1);
    }

    void test(String itemName, Integer maxPrice, Item... items) {
        List<Item> result = itemRepository.findAll(new ItemSearchCond(itemName, maxPrice));
        assertThat(result).containsExactly(items);
    }
}

앞서 우리가 Transaction을 하기 위해 했던 코드들이 다 사라졌다,

@Transactional 이 애노테이션 하나로 이제 트랜잭션이 된다.

근데 원래 트랜잭션이 끝나면 커밋을 하는 게 기본 아닌가? 할 수도 있다.

맞다. 근데 테스트를 만나면 다르다.

아마 @Test 이 애노테이션 보고 판단하는 것 같다.

 

@Transactional이 @Test를 만났을 때 기본은 commit이 아닌 rollback이다.

하도 테스트에서는 @Transactional을 사용할 일이 많으니 스프링이 그렇게 하다록 제공해 준 듯 하다.

 

 

또, 이런 생각이 들 수 있다.

만약 이렇게 트랜잭션을 시작하고 들어갔는데, (@Transactional 있으면 트랜잭션 하는거임.)

만약 호출한 Service등에 @Transactional이 있다면?

그럼 기존의 트랜잭션하고 있던 커넥션을 이어서 받는다.

 

사실 트랜잭션 안해도 (물론 트랜잭션 안하면 쓰레드로컬에 커넥션이 있을 리가 없지만) 커넥션 풀에서 커넥션을 가져오기 이전에 쓰레드로컬에서 커넥션을 가져오는 거라서 그냥 그렇게 동작 하는 듯.

@Transactional도 커넥션을 커넥션 풀에서 얻어오기 이전에 쓰레드로컬에 커넥션이 있으면 그거 쓰는 듯.

 

 

이제 다른 테스트들과 커넥션(DB의 세션)이 격리되었고, (또 DB자체도 테스트용 DB를 만들어 격리시켰고)

테스트가 끝나면 롤백되기에 반복해서 테스트를 실행할 수 있다.

 

만약 기본값이 아닌 Commit등을 하고 싶다면,

@Test 붙은 것에 @Commit을 붙이거나 @Rollback(false)를 붙이거나 둘 중 하나 하면 된다.

 

 

앞으로 DB관련 테스트 할 때는 @Transactional은 거의 짝꿍이라고 생각하면 된다.

뭐 어떤 의미인지, 어떤 동작을 하는 지 알면 짝꿍이고 뭐고를 떠나서 그냥 필요하면 쓰고 필요하지 않다면 안쓰고 그만이지만.

'스프링 > 6. 스프링 DB-2' 카테고리의 다른 글

17. 스프링부트의 임베디드 DB  (0) 2023.10.09
16. 임베디드 모드 DB  (0) 2023.10.09
14. 데이터 롤백  (0) 2023.10.09
13. 테스트용 DB와 서버용 DB 분리  (0) 2023.10.09
12. DB 접근 기술 테스트 하는 법  (0) 2023.10.09