1. 회원 기능 테스트

 

테스트 내용

-회원가입기능이 작동해야 한다.

-회원가입할 때 같은 이름이 있으면 예외가 발생해야 한다.

 

테스트케이스

1.MemberService 클래스에서

a.하기 MemberService에서 ctrl + shift + t < create test < ok를 한다.

 

public MemberService {

......

}

 

b. public void 회원가입( ) throws Exception{

//given(이렇게 주어졌을 때)

 

//when(이렇게 하면)

 

//then(이렇게 된다)

}

 

public void 회원_중복_예외() throws Exception{

//given

 

//when

 

//then

}

 

 

테스트 범위

-정말 순수한 단위테스트 보다 jpa가 메모리모드로 실제 디비까지 돌아가는 것을 보기 위해

 완전히 스프링과 integration해서 테스트를 할 것이다.

 

그래서 하기 두가지를 세팅할 것이다.

 

@RunWith(SpringRunner.class)

@SpringBootTest

@Transactional 데이터를 변경해야 하기 때문에 하는 설정,

public class MemberServiceTest {

 

//테스트라 참조해서 테스트할 것이 없기 때문에 하기처럼 작성

@Autowired MemberService memberService;

@Autowired MemberRepository memberRepository;

}

////alt + enter를 누르고 suppress인가를 누르니 memberRepository가 보라색으로 변경됨

 

*@Transanctional

-데이터를 변경해야 하기 때문에 하는 설정

-같은 Transaction안에서 같은 엔티티에서 id(pk)이 같으면 같은 영속성 컨텍스트에서

 똑같은 값이 관리가 된다.

-Transactional이 있어야 롤백이 가능하다.

-insert SQLDB에 날라가지 않는다. 영속성 컨텍스트가 flush가 되지 않는다.

 

참고: 테스트 케이스 작성

Given, When, Then (http://martinfowler.com/bliki/GivenWhenThen.html) >

필수는 아니지만 위에 것을 기본으로 해서 다양하게 응용하는 것을 권장한다고 한다.

 

 

*축약어를 통해 코드구문생성하기

설정경로

- File->Setting->Editer->Live Templates -> tdd 단축단어 생성 -> tdd

 

내용설정

@Test

public void () throws Exception {

//given

 

//when

 

//then

}

 

=> tdd를 사용하면 위에 구문이 자동생성된다.

 

 

@Rollback(false)

-Transacntional을 롤백이 기본적으로 되므로 직접 눈으로 확인하고 싶고

 DB에 값이 들어가는 것을 확인하고 싶을 때에는

 @Rollback(false)public void 회원가입() throws Exception 에 적용하자.

 

-데이터베이스 트랜잭션이 커밋을 하는 순간 flush가 되면서

  jpa 영속성 컨텍스트가 있는 member객체가 insert문이 만들어지면서 DB insert가 나가게 된다.

 

사진

 

em.flush();

assertEquals(member, memberRepository.findOne(savedId));

-insert문이 db로 가는 것을 실행시킬 때 눈으로 볼 수 있고,

 transactionrollback 되도록 하는 설정

 

try catch return

-중복회원 및 회원가입을 다시 테스트

*데이터베이스에 앞서 insertDB로 날려 값(PRIMARY KEY)이 생성 되었다면

 실행시 중복회원에서 에러가 발생하니 조심할 것

 

사진

회원 기능 테스트 완료

 

 

 

 

2. 회원기능테스트 기술적 정리

a.RunWith(SpringRunner.class)

-jUnit 실행할 때 스프링이랑 엮어서 실행을 하겠다.

 

@SpringBootTest

-스프링부트위에서 테스트를 하려면 꼭 필요하다.

-@Autowired 실행이 실패한다.

-컨테이너 안에서 테스트를 한다.

 

b.@Transactional

-테스트가 끝나면 Rollback을 한다.

-서비스클래스나 레포지터리에 붙여서 돌리면 Rollback하지 않는다.

 

c. tdd 단축키 만들어서 테스트구문 생성하기

 

d. 테스트를 완전 격리된 환경에서 하는 방법

-실제 외부에 있는 데이터베이스를 사용했다.

-디비를 외부에 설치해서 테스트해야 할 경우가 생긴다. 테스트가 끝나고

 데이터가 초기화 되는 것이 좋다.

-테스트를 완전 격리된 환경에서 하는 방법이 있다.

-자바안에 데이터베이스를 만들어 테스트 하는 방법이 있는데,

 메모리 데이터베이스를 사용하는 방법이다. 안에 않고 가상의 데이터베이스를 만들어서

 스프링부트를 사용하면 이러한 방법을 무료로 이용할 수 있다.

 

 

종류

1. main

- 실제 개발하는 운영소스 (java, resouces)

-기본적으로 운영로직은 java, resources가 운영권을 가진다.

 

2. test

-테스트하는 운영소스 (java, resources:directory resources생성해주어야 함)

-테스트 운영로직의 우선권은 test안에 java, resources가 우선권을 가진다.

-application.yml을 java->resource로부터 가져온다면 application.yml이 우선권을 가져

 main에 있는 application.yml은 무시되고 test안에 있는 application.yml이 실행된다.

 

(application.yml 복사 test->resources(directory)->application.yml(복사 from main:java)

 

 

 

JVM내에서 실행 및 메모리모드로 데이터베이스실행 테스트

-jvm 안에서 실행되고 memory모드로 데이터베이스를 띄워주기 위해

 application.ymlspingurl을 인메모리 url로 바꾸어 준다.

 

주소는 h2.database->cheat sheet->In-Memory->첫번째 라인

 

사진 h2.database 사이트

 

사진

 

*하지만, spring, jpa 설정 내용이 전혀 없고 logging,level 설정 내용만으로도

 스프링부트의 기능으로 인해 인메모리 모드로 실행이 된다.

 극단적으로 얘기해서 내용이 전혀 없어도 인메모리 모드로 실행이 된다.

회원서비스(MemberService)

 

순서별로 설명

a.@Service

- @service 어노테이션에 ctrl + b로 확인을 해보면 @component가 설정이 되어 있다.

@component의 스캔의 대상이 되어서 자동으로 스프링빈으로 등록이 된다.

 

b.@Transactional(readOnly = true)

-class 전체적용 여기에선 읽기가 많으므로, readOnly으로 설정

-jpa의 모든 데이터변경이나 모든 로직들은 Transactional 안에서 실행 되어야 한다.

 Transactional 설정으로 lazy loading이 가능해진다.

-클래스레벨의@Transacntional 사용시 public method가 적용범주 안에 속한다.

 

*Transactional 종류2가지 javax,

-스프링에 dependency한 로직이많아서 스프링이 제공하는

Transactional 어노테이션사용권장한다. 사용할수있는옵션들이많기 때문에

 

c.@Autowired

//MemberRepository 클래스를 Autowired로 스프링이 스프링빈에 등록되어 있는 MemberRepository를 인젝션해준다.

____________________________________

 

현재구문에 @Autowired 단점

-일단 테스트를 위해 변경을 못하고 액세스할 수 있는 방법이 없다. 그래서 setter 인젝션을 사용한다.

 

단점보완책

1.예시(setter injection)

@Autowired

public void setMemberRepository(MemberRepository memberRepository) {

this.memberRepository = memberRepository;

}

 

-setter injection을 사용, 테스트할때에 memberRepository 목에 직접 입력(임의의 가짜 repository를 주입)해줄 수 있다.

-단점은, 실제 서비스실행시 누군가 바꿀 수있다.

-실제 동작하고 있는데 사실 바꿀일은 없다. 그래서 이보완책은 좋지 않다.

-스프링데이터 jpa에 의해서 다음과 같이 줄이고 보완할 수 있다.

 

2.최적의 보완책 예시(생성자 injection)

@RequiredArgsConstructor 와 private final MemberRepository 설정

-private final MemberRepository memberRepository 권장

-MemberService 에 값을 작성안해주면 위의 memberRepository에 빨간줄이 생긴다.

-, compile 시점에 한번 더 점검 할 수 있다.

*@RequiredArgsConstructor (lombok 참고)

-final에 있는 필드만 가지고 생성자를 만들어준다. 권장.

________________________________________

 

d.@Transactional

-@Transactional defaultTransactional=false 이다. class전체적용이 있어도

 현재 지금의 어노테이션이 우선권을 가져 이것만 적용이 된다.

-읽기전용아닌 곳에서는 @Transactional(readOnly=true)를 하면 안된다.

 

회원가입

validateDuplicateMember(member); 중복 회원 검증 함수이름=Exception

-MemberRepository클래스에서 jpa에서 em.persist를 하면 순간에 영속성컨텍스트에 Member를 올린다.

-@GeneratedValue를 생성하면 id가 항상생성된 것을 보장한다.

 영속성 컨텍스트에 값을 넣어야 하는데 db와 매핑한 키값이 id(pk)값이 된다.

-db에 들어가지 않아도 그래서 memberid도 채워준다는 얘기

 

f.Exception(validateDuplicateMember (Member member)

List<Member> findMembers = memberRepository.findByName(member.getName());

-받아온 이름을 찾아볼 때에 반환값 설정을 findByName에서 단축키ctrl+alt+v-member이름을

 유니크제약조건을 적용해주어야한다. (현재는 적용이 안되어 있음)

 MemberA라는 이름으로 동시에 INSERT를 하면 같은 이름으로 가입이 가능하기 때문에

 

if (!findMembers.isEmpty()) {

throw new IllegalStateException("이미 존재하는 회원입니다.");

}

 

-member이름을 찾아(!findMembers)보고 없으면(isEmpty)이면 throw new

이미존재하는 회원이라고 출력한다.

 

g.회원전체조회 작성

public List<Member> findMembers() and public Member findOne(Long memberId)

-@Transactional(readOnly=true)로 줄 경우 멤버를 찾을 때에 읽기전용으로만 사용하므로

 jpa가 사용되는 곳에서는 성능을 최적화 한다. 리소스를 적게사용한다.

-읽기전용아닌 곳에서는 @Transactional(readOnly=true)를 하면 안된다.

 변경이 안된다.

 

h. MemberRepository 클래스에서

-스프링부트 라이브러리, 스프링데이터 jpa의 지원으로 하기와 같이 가능해져

@Autowired

private EntityManager em;

public MemberRepository(EntityManager em) {

// Alt + Insert 키 이용해서 Generator사용

this.em = em;

}

 

위의 내용을 @RequiredArgsConstructorfinal 조합으로 사용할 수 있다.

*원래는 @Autowired@PersistentContext(이것도 잇어야 하지만, 생략가능하다.

스프링부트에서 있는 것으로 지원을 해주기때문(현재 버전에서 지원가능한지를 체크해야 함)

 

 

@Repository

@RequriedArgsConstructor

public class MemberRepository {

 

private final EntityManager em;

}

으로 줄일 수 있다.

 

사진

목차

회원 도메인 개발

-구현 기능

-회원 등록

-회원 목록 조회

 

순서

-회원 엔티티 코드 다시 보기

-회원 리포지토리 개발

-회원 서비스 개발

-회원 기능 테스트

 

회원도메인개발

1. 회원엔티티 코드 다시보기

 

2. MemberRepository

사진

a.레포지터리 패키지 생성

b.MemberRepository 클래스 생성

c. @Repository 어노테이션 세팅

*스프링에서제공하는 Repository 어노테이션을 설정해주면, 

component 스캔에의해서 스프링빈으로 자동으로 관리가된다.

 

*스프링빈

-자바 객체

-스프링 컨테이너(Spring Container)에 의해서 만들어진 자바 객체를스프링 빈이라고 부른다.

-스프링 빈과 자바 일반 객체와의 차이점은 없다굳이 다른점이라고 한다면, 컨테이너에 의해 만들어졌다.

 

*스프링 컨테이너가 나오게 된 배경

A a = new A( );

A a1 = new A( );

A a2 = new A( );

각각의 객체를 생성해주도록 코드를 작성하여 메모리를 3번 낭비했었는데,

메모리를 효율적으로 관리해주고자 메모리를 1번만 낭비를 하게 하기 위해 스프링 컨테이너가 탄생했다.

스프링 컨테이너에 스프링빈을 넣어 객체를 하나만 생성하여 공통 객체역할로 이 객체를 가지고 3개의 객체를 @Autowired를 활용해 생성한다.

 

예)

@Autowired

a a;

 

@Autowired

a1 a1; 

 

@Autowired

a2 a2; 

 

 

-@PersistenceContext 설정 및 private EntityManager em;

스프링이 엔티티 매니저를 만들어서 em에 인젝션 주입을 해준다.

 

d. 조회하는로직을 작성한다.(save, find)

save

-기본 엔티티매니저 사용은 persist 영속성 컨텍스트에 멤버엔티티객체를 넣고

후에 트랜잭션이 커밋 되는 시점에 디비에 인서트쿼리가 날라가면서 반영된다.

 

find메서드 사용

-단건조회

return em.find(Member.class : 첫번째 타입, id :primary key를 넣어준다.)

 

 

e. 회원전체 로직을 작성한다.(find all)

-createQuery다음에""를타입할것. 첫번째 jpql를 사용 뒤에는 반환타입

-하기코드를합친다.

( return result;에서 단축키 ctrl + alt + n < inline all < refactor )

List<Member> result = em.createQuery("select m from Member m", Member.class) .getResultList(); return result;

 

확인

*jpqlsql 차이점

-기능적으로 동일하고 결국 sql로 번역한다.

-sqlfrom의 대상이 테이블대상으로 쿼리를 한다.

-jpqlfrom의 대상이 엔티티객체를 대상으로 쿼리를 한다. (사진에 있는 Member를 조회한다.)

 

f. parameter binding에 의해서 특정 회원만 찾는 코드

-setParameterintPosition으로설정

+ Recent posts