You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
save(), updateItem() 는 계속 돌려도 계속 성공함
그러나 findItems() 는 디비에 데이터가 쌓이면 실패하는 구조임.
3개 저장 후 3개만 조회되는지 테스트 하는데 지금까지 저장한 모든 데이터가 나오기 때문.
TestDataInit 에서 넣어주는 것엔 @Profile("local") 로 지정했기 때문에 Test 로 프로필을 지정한 테스트에선 들어가지 않음. 초기화 데이터 때문에 문제가 생긴건 아님.
즉 이미 과거에 저장한 데이터 때문에 실패하게됨.
테스트에서 중요한건 격리성! 테스트를 할 때 데이터가 없어야함 (깔끔해야함).
지금은 실제 사용 서버와 테스트 서버가 동일한 디비를 바라보기 때문에 깔끔할 수 없음.
그럼 이를 분리해보자.
테스트 - 데이터베이스 분리
로컬에서 사용하는 애플리케이션 서버와 테스트에서 같은 데이터베이스를 사용하고 있으니 테스트에서 문제가 발생한 다.
이런 문제를 해결하려면 테스트를 다른 환경과 철저하게 분리해야 한다.
가장 간단한 방법은 테스트 전용 데이터베이스를 별도로 운영하는 것이다. H2 데이터베이스를 용도에 따라 2가지로 구분하면 된다.
jdbc:h2:tcp://localhost/~/test local에서 접근하는 서버 전용 데이터베이스
jdbc:h2:tcp://localhost/~/testcase test 케이스에서 사용하는 전용 데이터베이스
데이터베이스 파일 생성 방법
데이터베이스 서버를 종료하고 다시 실행한다.
사용자명은 sa 입력 JDBC URL에 다음 입력, jdbc:h2:~/testcase (최초 한번) ~/testcase.mv.db 파일 생성 확인
이후부터는 jdbc:h2:tcp://localhost/~/testcase 이렇게 접속
테이블 생성하기 testcase 데이터베이스에도 item 테이블을 생성하자.
drop table if exists item CASCADE;
create table item
(
id bigint generated by default as identity,
item_name varchar(10),
price integer,
quantity integer,
primary key (id)
);
스프링은 테스트 데이터 초기화를 위해 트랜잭션을 적용하고 롤백하는 방식을 @Transactional 애노테이션 하나로 깔끔하게 해결해준다.
원래 봤던것과 같은데, 이를 테스트에서 사용하면 조금 특별하게 사용된다.
직전에 추가했던 코드 주석처리 후, 테스트 클래스 위에 @transactional 를 붙여준다.
그러면 정상 실행하는것 확인 가능.
기존에 사용하던 @transactional 은 성공적으로 수행시 커밋하도록 동작했음.
그런데 @transactional 애노테이션을 테스트에서 사용하면 아주 특별하게 동작한다. @transactional 이 테스트에 있으면 스프링은 테스트를 트랜잭션 안에서 실행하고, 테스트가 끝나면 트랜잭션을 자동으로 롤백시켜 버린다!
테스트에 @Transactional 애노테이션이 테스트 메서드나 클래스에 있으면 먼저 트랜잭션을 시작한다.
테스트 로직을 실행한다. 테스트가 끝날 때 까지 모든 로직은 트랜잭션 안에서 수행된다.
트랜잭션은 기본적으로 전파되기 때문에, 리포지토리에서 사용하는 JdbcTemplate도 같은 트랜잭션을 사용한다.
테스트 실행 중에 INSERT SQL을 사용해서 item1 , item2 , item3 를 데이터베이스에 저장한다.
물론 테스트가 리포지토리를 호출하고, 리포지토리는 JdbcTemplate을 사용해서 데이터를 저장한다.
검증을 위해서 SELECT SQL로 데이터를 조회한다. 여기서는 앞서 저장한 item1 , item2 , item3 이 조회되었다.
커밋을 하지 않아도 조회가 가능한 이유는 나의 트랜젝션 이기 때문에.
SELECT SQL도 같은 트랜잭션을 사용하기 때문에 저장한 데이터를 조회할 수 있다. 다른 트랜잭션에서는 해당 데이터를 확인할 수 없다.
여기서 assertThat() 으로 검증이 모두 끝난다.
@Transactional 이 테스트에 있으면 테스트가 끝날때 트랜잭션을 강제로 롤백한다.
롤백에 의해 앞서 데이터베이스에 저장한 item1 , item2 , item3 의 데이터가 제거된다.
테스트 케이스의 메서드나 클래스에 @transactional 이 붙은 경우에 이렇게 된다.
그런데 리포지토리나, 서비스 에 붙은 @transactional은?
이미 테스트에서 트렌젝션이 시작되었기 때문에, 리포지토리나 서비스 의 코드가 시작될 때 원래 열려있던 테스트의 트렌젝션에 참여하게 되고,
테스트의 트렌젝션 이기 때문에 롤백된다.
테스트가 끝난 후 개발자가 직접 데이터를 삭제하지 않아도 되는 편리함을 제공한다.
테스트 실행 중에 데이터를 등록하고 중간에 테스트가 강제로 종료되어도 걱정이 없다. 이 경우 트랜잭션을 커밋 하지 않기 때문에, 데이터는 자동으로 롤백된다. (보통 데이터베이스 커넥션이 끊어지면 자동으로 롤백되어 버린 다.)
트랜잭션 범위 안에서 테스트를 진행하기 때문에 동시에 다른 테스트가 진행되어도 서로 영향을 주지 않는 장점이 있다.
@Transactional 덕분에 아주 편리하게 다음 원칙을 지킬수 있게 되었다.
테스트는 다른 테스트와 격리해야 한다.
테스트는 반복해서 실행할 수 있어야 한다.
그런데 막상 하다보면, 저장된걸 직접 보고싶은 경우가 있음. 이런 경우 @Commit 을 붙여주면 됨.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
테스트 - 데이터베이스 연동
데이터 접근 기술을 테스트 하는 방법에 대해 알아보자.
JDBCTemplate 으로 개발한 다음에 테스트 코드로 확인해본적이 없음. (기능은 확인해봤지만)
데이터 베이스를 연동한 상태에서 테스트를 어떻게 할거냐!?
이제부터 데이터베이스를 연동해서 테스트 해볼 예정, 앞서 개발한 ItemRepository 를 통해서.
test 의 application.properties 에는 아직 데이터베이스 설정이 없어 추가해줘야함.
@SpringBootTest 가 있으면 @SpringBootApplication 을 찾아내서 설정으로 사용함.
우리의 @SpringBootApplication 에는 @import(JdbcTemplateV3Config.class) 가 있으므로 이 설정으로 JDBCTemplate 을 사용해 테스트를 함.
실행해보면 통과하는 테스트도 있고, 실패하는 것도 있음.
save(), updateItem() 는 계속 돌려도 계속 성공함
그러나 findItems() 는 디비에 데이터가 쌓이면 실패하는 구조임.
3개 저장 후 3개만 조회되는지 테스트 하는데 지금까지 저장한 모든 데이터가 나오기 때문.
TestDataInit 에서 넣어주는 것엔 @Profile("local") 로 지정했기 때문에 Test 로 프로필을 지정한 테스트에선 들어가지 않음. 초기화 데이터 때문에 문제가 생긴건 아님.
즉 이미 과거에 저장한 데이터 때문에 실패하게됨.
테스트에서 중요한건 격리성! 테스트를 할 때 데이터가 없어야함 (깔끔해야함).
지금은 실제 사용 서버와 테스트 서버가 동일한 디비를 바라보기 때문에 깔끔할 수 없음.
그럼 이를 분리해보자.
테스트 - 데이터베이스 분리
로컬에서 사용하는 애플리케이션 서버와 테스트에서 같은 데이터베이스를 사용하고 있으니 테스트에서 문제가 발생한 다.
이런 문제를 해결하려면 테스트를 다른 환경과 철저하게 분리해야 한다.
가장 간단한 방법은 테스트 전용 데이터베이스를 별도로 운영하는 것이다. H2 데이터베이스를 용도에 따라 2가지로 구분하면 된다.
jdbc:h2:tcp://localhost/~/testlocal에서 접근하는 서버 전용 데이터베이스jdbc:h2:tcp://localhost/~/testcasetest 케이스에서 사용하는 전용 데이터베이스데이터베이스 파일 생성 방법
데이터베이스 서버를 종료하고 다시 실행한다.
사용자명은
sa입력 JDBC URL에 다음 입력,jdbc:h2:~/testcase(최초 한번)~/testcase.mv.db파일 생성 확인이후부터는
jdbc:h2:tcp://localhost/~/testcase이렇게 접속테이블 생성하기
testcase데이터베이스에도item테이블을 생성하자.이후 test 의 application.properties 에
이후에 findItems() 실행하면 성공함.
그런데 다시 실행하면 또 실패함.
테스트 데이터가 축적되기 깨문에.
테스트에서 매우 중요한 원칙은 다음과 같다.
테스트 마지막에 삭제하는 로직을 넣어도 되겠지만, 만약 중간에 오류가 나서 삭제 로직이 수행되지 못하면 이를 호출하지 못할 수 있음. 그래서 근본적인 해결책이 아님.
이를 해결하는 방법 = 커밋, 롤백 활용하기
테스트 - 데이터 롤백
트랜잭션과 롤백 전략
이때 도움이 되는 것이 바로 트랜잭션이다.
아래와 같은 순서로 테스트 진행
테스트는 각각의 테스트 실행 전 후로 동작하는
@BeforeEach,@AfterEach라는 편리한 기능을 제공한다.테스트에 트랜잭션과 롤백을 적용하기 위해 다음 코드를 추가하자.
로그를 확인하면 트랜잭션 시작하고 롤백 되는거 확인 가능.
데이터를 봐도 없는것 확인 가능.
PlatformTransactionManager를 주입 받아서 사용하면 된다. 참고로 스프링 부트는 자동으로 적절한 트랜잭션 매니저를 스프링 빈으로 등록해준다. (앞서 학습한 스프링 부트의 자동 리소스 등록 장을 떠올려보자.)@BeforeEach: 각각의 테스트 케이스를 실행하기 직전에 호출된다. 따라서 여기서 트랜잭션을 시작하면 된다.그러면 각각의 테스트를 트랜잭션 범위 안에서 실행할 수 있다.transactionManager.getTransaction(new DefaultTransactionDefinition())로 트랜잭션을 시작한다.@AfterEach: 각각의 테스트 케이스가 완료된 직후에 호출된다. 따라서 여기서 트랜잭션을 롤백하면 된다. 그러면 데이터를 트랜잭션 실행 전 상태로 복구할 수 있다.transactionManager.rollback(status)로 트랜잭션을 롤백한다.그런데 이부분이 약간 불편함. 이를 편리하게 해결하는 방법이 또 있음..!
테스트 - @transactional
스프링은 테스트 데이터 초기화를 위해 트랜잭션을 적용하고 롤백하는 방식을
@Transactional애노테이션 하나로 깔끔하게 해결해준다.원래 봤던것과 같은데, 이를 테스트에서 사용하면 조금 특별하게 사용된다.
직전에 추가했던 코드 주석처리 후, 테스트 클래스 위에 @transactional 를 붙여준다.
그러면 정상 실행하는것 확인 가능.
기존에 사용하던 @transactional 은 성공적으로 수행시 커밋하도록 동작했음.
그런데 @transactional 애노테이션을 테스트에서 사용하면 아주 특별하게 동작한다.
@transactional 이 테스트에 있으면 스프링은 테스트를 트랜잭션 안에서 실행하고, 테스트가 끝나면 트랜잭션을 자동으로 롤백시켜 버린다!
@Transactional애노테이션이 테스트 메서드나 클래스에 있으면 먼저 트랜잭션을 시작한다.item1,item2,item3를 데이터베이스에 저장한다.item1,item2,item3이 조회되었다.assertThat()으로 검증이 모두 끝난다.@Transactional이 테스트에 있으면 테스트가 끝날때 트랜잭션을 강제로 롤백한다.item1,item2,item3의 데이터가 제거된다.테스트 케이스의 메서드나 클래스에 @transactional 이 붙은 경우에 이렇게 된다.
그런데 리포지토리나, 서비스 에 붙은 @transactional은?
이미 테스트에서 트렌젝션이 시작되었기 때문에, 리포지토리나 서비스 의 코드가 시작될 때 원래 열려있던 테스트의 트렌젝션에 참여하게 되고,
테스트의 트렌젝션 이기 때문에 롤백된다.
@Transactional덕분에 아주 편리하게 다음 원칙을 지킬수 있게 되었다.그런데 막상 하다보면, 저장된걸 직접 보고싶은 경우가 있음. 이런 경우 @Commit 을 붙여주면 됨.
All reactions