회원기능 테스트에서 질문드립니다.
해결된 질문2019-10-06T05:37:01.129Z
2212
0
dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-devtools' compile( 'com.github.gavlyukovskiy:p6spy-spring-boot-starter:1.5.7' ) compileOnly 'org.projectlombok:lombok' runtimeOnly 'com.h2database:h2' annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' }
spring : datasource : url : jdbc:h2:tcp://localhost/~/Dev/projects/book/db;MVCC=TRUE username : sa password : driver-class-name : org .h2 .Driver jpa : hibernate : ddl-auto : create properties : hibernate : show_sql : true format_sql : truelogging : level : org.hibernate.SQL : debug org.hibernate.type : trace
package com.ym.book.shop.service ; import com.ym.book.shop.domain.entity.Member ; import com.ym.book.shop.repository.MemberRepository ; import org.junit. Test ; import org.junit.runner. RunWith ; import org.springframework.beans.factory.annotation. Autowired ; import org.springframework.boot.test.context. SpringBootTest ; import org.springframework.test.context.junit4.SpringRunner ; import org.springframework.transaction.annotation. Transactional ; import static org.junit.Assert.* ; @RunWith (SpringRunner. class ) @SpringBootTest @Transactional public class MemberServiceTest { @Autowired MemberService memberService ; @Autowired MemberRepository memberRepository ; @Test public void 회원가입 () throws Exception{ //given Member member = new Member() ; member.setName( "Kim" ) ; //when Long saveId = memberService .join(member) ; //then assertEquals (member , memberRepository .findOne(saveId)) ; } @Test public void 중복_회원_예외 () throws Exception{ //given //when //then } } 결과 : 2019-10-06 14:52:56.074 INFO 2244 --- [ Test worker] o.h.h.i.QueryTranslatorFactoryInitiator : HHH000397: Using ASTQueryTranslatorFactory
2019-10-06 14:52:56.173 DEBUG 2244 --- [ Test worker] org.hibernate.SQL :
select
member0_.member_id as member_i1_4_,
member0_.city as city2_4_,
member0_.street as street3_4_,
member0_.zipcode as zipcode4_4_,
member0_.name as name5_4_
from
member member0_
where
member0_.name=?
Hibernate:
select
member0_.member_id as member_i1_4_,
member0_.city as city2_4_,
member0_.street as street3_4_,
member0_.zipcode as zipcode4_4_,
member0_.name as name5_4_
from
member member0_
where
member0_.name=?
2019-10-06 14:52:56.204 DEBUG 2244 --- [ Test worker] org.hibernate.SQL :
insert
into
member
(member_id, city, street, zipcode, name)
values
(null, ?, ?, ?, ?)
Hibernate:
insert
into
member
(member_id, city, street, zipcode, name)
values
(null, ?, ?, ?, ?)
2019-10-06 14:52:56.216 INFO 2244 --- [ Test worker] o.s.t.c.transaction.TransactionContext : Rolled back transaction for test: [DefaultTestContext@4d40c3e testClass = MemberServiceTest, testInstance = com.ym.book.shop.service.MemberServiceTest@b322034, testMethod = 회원가입@MemberServiceTest, testException = [null], mergedContextConfiguration = [WebMergedContextConfiguration@1ee9b049 testClass = MemberServiceTest, locations = '{}', classes = '{class com.ym.book.shop.ShopApplication}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}', contextCustomizers = set[org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@275315df, org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@47bf348f, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@7db5eaa6, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@2cda0ac3], resourceBasePath = 'src/main/webapp', contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map['org.springframework.test.context.web.ServletTestExecutionListener.activateListener' -> true, 'org.springframework.test.context.web.ServletTestExecutionListener.populatedRequestContextHolder' -> true, 'org.springframework.test.context.web.ServletTestExecutionListener.resetRequestContextHolder' -> true]]
2019-10-06 14:52:56.223 INFO 2244 --- [ Thread-6] o.s.s.concurrent.ThreadPoolTaskExecutor : Shutting down ExecutorService 'applicationTaskExecutor'
2019-10-06 14:52:56.223 INFO 2244 --- [ Thread-6] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2019-10-06 14:52:56.226 INFO 2244 --- [ Thread-6] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2019-10-06 14:52:56.231 INFO 2244 --- [ Thread-6] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
BUILD SUCCESSFUL in 5s
5 actionable tasks: 3 executed, 2 up-to-date
2:52:56 PM: Tasks execution finished ':cleanTest :test --tests "com.ym.book.shop.service.MemberServiceTest.회원가입"'. 이렇게 결과가 나옵니다. select, insert가 2번씩 실행이 되는데요 혹시 제가 설정이 잘못된 게 있을까요?
답변 6
6
김영한
2019-10-06T14:38:57.388Z
안녕하세요 kim님 연타성 질문이니 하나씩 답을 드릴께요^^
1. 다른 프로젝트랑 비교해봤는데 보통 [ main] 이런식으로 쓰레드를 표기하는데 제꺼는 [ test worker]로 쓰레드가 표기되네요 혹시 gradle version이 문제일까요?
최근 intellij 버전이 올라가면서 자바로 바로 실행해야 하는데, gradle로 실행하는게 디폴트가 되었습니다. 다시 자바로 바꾸려면 다음처럼 하시면 됩니다^^
Preferences 메뉴에 들어가셔서
Build, Execution, Depolyment -> Build Tools -> Gradle에 들어가면
Build And run using: -> 이것을 Gradle, Intellij 중에서 Intellij를 선택해주면 됩니다.
Run Test using: -> 이것도 Gradle, Intellij 중에서 Intellij를 선택해주면 됩니다.
만약 잘 안되면 다음 쓰레드를 읽어주세요
https://www.inflearn.com/questions/14470
2. show-sql이 보니깐 강사님이 주석 처리하셨던데 저도 주석처리하니 1개로 정상적으로
-> show-sql은 System.out으로 출력하기는 것이기 때문에, 가급적이면 로그로 출력하는 기능을 사용하는게 좋습니다. 실무에서는 모든 출력을 로그를 통해서 관리해야 합니다.
3. insert문이 저는 실행이 됩니다. join에서 영속성을 갖고 Long id가 생성되고 findOne을 할 때 같은 Id가 있기때문에 insert select가 아닌 영속성 콘텍스트에서 가지고와서 비교를 하고 마지막은 transaction이 끝나므로 insert가 실행된다고 생각하면 될까요?
-> JPA는 영속성 컨텍스트에 엔티티를 등록할 때 항상 PK 값이 있어야 합니다. 그런데 키 생성 전략을 IDENTITY로 두면 데이터베이스에 INSERT를 해야 PK를 구할 수 있습니다. IDENTITY라는 것이 데이터베이스에 PK 생성을 위임하는 것이기 때문이지요. 대표적으로 MySQL에 auto increment가 있습니다. 그래서 IDENTITY 전략은 PK를 구하기 위해 어쩔 수 없이 DB에 INSERT를 강제로 먼저 하게 됩니다. 참고로 나머지 전략은 DB에 INSERT를 강제로 하지 않습니다.
관련해서 더 자세한 내용은 자바 ORM표준 JPA 프로그래밍 강의에 있는 기본 키 매핑 부분을 참고해주세요.
4. @Transactional을 지워주면 fail이 납니다.
-> 네 맞습니다^^ fail이 납니다! 이것을 이해하는게 정말 중요합니다. 결론부터 말씀드리면 트랜잭션 당 하나의 영속성 컨텍스트가 적용됩니다. 다르게 말하면 트랜잭션이 달라지면 영속성 컨텍스트도 달라집니다.
스프링은 기본적으로 @Transactional을 사용하면 시작부터 끝까지 트랜잭션을 하나로 묶어서 전파합니다. 여기서 테스트에 @Tx(Transactional)가 있으면 이 테스트 시작부터 끝날 때 까지 모든 트랜잭션은 별도의 옵션이 없으면 다 하나로 묶입니다. 결론적으로 영속성 컨텍스트도 하나로 유지됩니다.
그런데! 테스트에서 @Tx를 제거하면 어떻게 될까요?
memberService.join()에서 사용하는 트랜잭션과 memberRepository.findOne()에서 사용하는 트랜잭션이 묶이지 않기 때문에 서로 완전히 상관없는 트랜잭션이 됩니다. 결과적으로 영속성 컨텍스트도 각각 다르기 때문에, 서로 다른 객체를 저장하고 있는 것이지요.
관련해서 더 자세한 내용은 자바 ORM 표준 JPA 프로그래밍 책 13.1 트랜잭션 범위의 영속성 컨텍스트 부분과 15.2 엔티티 비교 부분을 참고하시면 됩니다.
감사합니다^^
1
kim
2019-10-06T23:40:12.382Z
감사합니다. 이해가 돠었습니다
0
kim
2019-10-06T06:53:39.434Z
1번 질문에 대한 추가 내용입니다.
아마도 생성전략이 달라서 그런것 같습니다.
이렇게 이해하면 될가요?
@Id @GeneratedValue (strategy = GenerationType.IDENTITY )@Column (name = "member_id" )private Long id ;
0
kim
2019-10-06T06:45:56.119Z
추가적인 질문인데요.
1. insert문이 저는 실행이 됩니다. join에서 영속성을 갖고 Long id가 생성되고 findOne을 할 때 같은 Id가 있기때문에 insert select가 아닌 영속성 콘텍스트에서 가지고와서 비교를 하고 마지막은 transaction이 끝나므로 insert가 실행된다고 생각하면 될까요?
2. @Transactional을 지워주면 fail이 납니다.
expected:<com.ym.book.shop.domain.entity.Member@3f9bc39c> but was:<com.ym.book.shop.domain.entity.Member@b8a4a4b>
Expected :com.ym.book.shop.domain.entity.Member@3f9bc39c
Actual :com.ym.book.shop.domain.entity.Member@b8a4a4b
이런식으로 나는데요. 이해가 정확히 되지가 않습니다.
0
kim
2019-10-06T06:39:40.455Z
show-sql이 보니깐 강사님이 주석 처리하셨던데 저도 주석처리하니 1개로 정상적으로 보입니다.
혹시나 저와 같은 궁금증이 생기실 분도 계실거 같아 이 질문은 그대로 두겠습니다.
0
kim
2019-10-06T06:16:54.620Z
다른 프로젝트랑 비교해봤는데 보통 [ main] 이런식으로 쓰레드를 표기하는데 제꺼는 [ test worker]로 쓰레드가 표기되네요 혹시 gradle version이 문제일까요?
OrderServiceTest 상문주문 테스트 시 update 쿼리 문의
sdk 설정 오류
오탈자 - @Transactional
src/test/resources 테스트 경로 문제
상품 등록후 H2 db 출력 순서 바꿀 수 있나요?
MemberRepositoryTest 실행오류
boot 4.x >>> trasasction rolled back log & p6spy(영한님, 수업 자료 업데이트 해주시면 감사하겠습니다!!)
강의 마지막 QueryDSL 사용 부분 질문있습니다
클라이언트에서 isbn과 author 수정 요청을 한 경우에 대해 질문드립니다.
도메인 모델 패턴 vs 트랜잭션 스크립트 패턴
기본 생성자
h2 DB 연결시 jdbc url 변경 이유가 궁금합니다.
멤버서비스테스트 부분에서 막힙니다.
실무에서도 EntityManager를 이용해서 많이 작업하는 편일까요?
초반에 h2 다운로드 과정 꼭 필요한가요?
자신 필드에도 get으로 접근하는 이유가 있을까요?
24분 27초 연관관계 편의 메서드 위치
단건 주문만 가능하게 한건 의도한 부분이신가요?
빌드 툴, Gradle
h2연결은 된 것 같은데 엔티티 테이블까지 작성 후 확인해보아도 테이블이 안보입니다
Repository에서 EntityManager 주입 방식 차이
롬복과 사용자 정의 setter 메서드
주문 목록 조회 fetch join 질문드립니다
dirty checking 질문드립니다.