ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • db 테스트에서의 트랜잭션
    스프링/스프링 기초DB 2023. 3. 25. 16:18

    db를 테스트코드를 이용해서 테스트해 볼 것이다.

     

     

    test에 있는 application.properties에

    spring.profiles.active=test
    
    spring.datasource.url=jdbc:h2:tcp://localhost/~/testcase
    spring.datasource.username=sa
    spring.datasource.password=

    를 추가해줘서 데이터베이스에 연결할 수 있도록 해줬다.

    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);
    }

    item을 저장하는 테스트 코드이다

     

    @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());
        }

    item을 수정하는 테스트 코드이다.

     

        @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);
        }
    }​

    findItems를 하는 테스트 코드이다.

     

    위의 저장과 수정은 오류가 나지 않지만 findItems()를 실행하면 오류가 난다. 그 이유는 db에 계속해서 데이터가 누적되기 때문이다. save에서 ItemA가 db에 저장되었고, update에서 Item1이 db에 저장되었다. 그런데 test(null, null, item1, item2, item3);이라는 문장은 item1, item2, item3만 test에 있다는 것을 확인하기 때문에 오류가 발생한다.

     

    테스트는 다른 테스트와 격리해서 진행해야하고, 반복해서 실행할 수 있어야한다.

     

    일단 h2콘솔에서 jdbc:h2:tcp://localhost/~/test대신 jdbc:h2:tcp://localhost/~/testcase 경로로 다른 db를 만들고, 반복해서 실행할 수 있도록 롤백해줘야한다. 이럴 때 사용하는 것이 트랜잭션이다.

     

     

     

    @Transactional 애노테이션

    @Transactional
    @SpringBootTest
    class ItemRepositoryTest {

    클래스 위에 @Transactional이라는 애노테이션을 추가해주면 된다.

    일반적으로는 로직이 성공적으로 실행되면 커밋을 하고, 아니면 롤백을 해준다. 하지만 테스트 코드에서는 로직이 성공해도 롤백을 해주는 기능이 별도로 있다.

     

     

    @Commit
    // @Rollback(false)
    @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);
    }

    만약에 트랜잭션을 사용하는데 별도로 커밋을 통해 확인하고 싶은 경우가 있을 수도 있다. 다른 기능은 직접 보지 않아도 상관없는데 상품을 저장하는 로직은 한 번 눈으로 확인하고 싶다는 생각이 된다면 해당 테스트 메서드 위에 @Commit이나 @Rollback(false) 애노테이션을 붙이면 된다.

     

     

     

    테스트 케이스에도 트랜잭션 애노테이션이 있고, 리포지토리에도 트랜잭션 애노테이션이 있다고 할 지라도 같은 트랜잭션을 공통으로 이용한다. 이 개념은 나중에 따로 포스팅으로 다루도록 하겠다.

     

     

     

     

    스프링 임베디드 데이터베이스

    spring.profiles.active=test
    
    # spring.datasource.url=jdbc:h2:tcp://localhost/~/testcase
    # spring.datasource.username=sa
    # spring.datasource.password=

    test에 있는 application.properties에서 spring.datasource를 전부 주석처리 해주면 db에 연결하지 않고 스프링에서 자체로 자바가 올라올 때 해당 db 테스트 케이스를 만들어주고, 자바가 내려가면 케이스를 삭제시켜준다. 메모리모드로 일시적으로 테스트해주는 것이다.

    테스트를 할 때에 계속 창을 띄우지않아도 테스트 코드가 잘 작동하는지 확인할 수 있다.

Designed by Tistory.