TDD / Mybatis - 전체 회원 수 조회
TDD
- Test Driven Development
- 테스트 주도 개발
- TDD 이 블로그에 많은 정보가 있다. 아직은 무슨 말인지 잘 모르겠다.
- 설계 > 테스트 코드 작성 > 구현 코드 작성
- 기존) 설계 > 구현 코드 작성 > 테스트 진행
테스트 코드는 여기에 작성한다.
jUnit
- Java 단위 테스트 프레임워크
프로젝트 우클릭 하고 해당 메뉴를 클릭하면 테스트가 가능하다.
-
테스트 코드가 잘 수행 되면 초록색, 반대일 경우 빨간색으로 표시된다.
<!-- pom.xml -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.8.2</version>
<scope>test</scope>
</dependency>
전에 pom.xml 파일에 추가했던 jUnit 라이브러리를 이 때 사용하는 거였네.
현재 프로젝트가 테스트가 가능한 환경인지 확인
@Test
public void nothing() {
// 아무 내용 없음
}
sqlSession 생성 테스트
@Test
@DisplayName("SqlSession 생성 테스트")
public void create() {
assertNotNull(session);
}
Annotation
- @Test : test
- @DisplayName : 테스트명 지정
- @Disabled : test에서 제외
- 실습 때는 테스트 가능한 환경인지 제일 처음에만 확인했고, 다음 테스트 진행 시에는 필요 없기 때문에 이 어노테이션을 지정했다.
- 아예 삭제하지않고 어노테이션을 사용한 이유는 다른 작업자도 다른 환경에서 이 코드를 사용할 수 있기 때문에
- @BeforeAll : 테스트 메서드가 실행되기 전에 한 번만 실행
- @BeforeEach : 각 테스트 메서드가 실행되기 전에 무조건 실행
MemberSevice / MemberServiceTest 생성 및 작성
- com.kh.member.model.service 패키지 생성 후 MemberSerivce.java 파일 생성
- 클래스명에 커서 올려놓고 Ctrl + 1 키를 누르면 아래와 같이 나오고 표시된 것을 클릭하면 test 폴더에 MemberServiceTest 파일이 자동으로 생성
-
패키지도 동일하게 test 파일이 생성되었다.
테스트 환경 확인
- 일단 테스트가 가능한 환경인지 체크
- 테스트 클래스에도 DisplayName 어노테이션 지정 가능
@DisplayName("Member Test") // 테이스명 지정
class MemberServiceTest {
@Test
@Disabled
public void nothing() {
// 테스트가 가능한 환경인지 확인
}
}
AssertJ
- 자바 JUnit의 테스트코드에 사용되어, 테스트코드의 가독성과 편의성을 높여 주는 라이브러리
import static org.assertj.core.api.Assertions.*;
import org.junit.jupiter.api.Test;
import.
AssertJ의 assertThatd() 메서드를 사용했다.
Service 객체 확인
- assertThatd(): Junit 4.4부터 추가된 메서드
- assertThat 참고
import static org.assertj.core.api.Assertions.assertThat;
@DisplayName("Member Test")
class MemberServiceTest {
// service 객체
private MemberService service;
@Test
@Disabled
public void nothing() {
}
@Test
public void create() {
// assertThat()을 이용하여 service 객체가 정상적으로 생성 되는지 확인
assertThat(service).isNotNull();
}
}
위 처럼 작성시 false이 뜨면서 test를 통과하지 못한다.
이유는 service에 값을 할당하지 않았기 때문에 null이 담겨 있다.
isNotnull = null 아니다. null이 아닐 경우에만 test가 통과된다.
-
해결 방법은 service에 값 할당하기.
실습 때는 test가 실행될 때마다 service를 새로 생성하도록 했다.
@DisplayName("Member Test")
class MemberServiceTest {
private MemberService service;
@Test
@Disabled
public void nothing() {
}
// 각 테스트 메서드가 실행되기 전에 무조건 실행
@BeforeEach
public void setup() {
// service 생성
service = new MemberService();
}
@Test
public void create() {
assertThat(service).isNotNull();
}
}
test 메서드를 실행하기 전에 service를 새로 생성했기 때문에
이제 jUnit 테스트를 돌리면 통과되어 초록색으로 표시된다.
회원수 조회 테스트
- MemberServiceTest 파일에 테스트 메서드 생성
// MemberServiceTest
public void getMemberCountTest() {
int count = service.getMemberCount();
assertThat(count).isPositive().isGreaterThanOrEqualTo(2);
}
int 타입의 count 변수에 회원수를 담아준다.
그리고 assertTht()으로 확인한다.
- isPositive() : 양수
- isGreaterThanOrEqualTo(2) : count가 2보다 클 것이라고 예상
- 처음엔 return 0; 으로 테스트 진행
- count는 2보다 커야하기 때문에 테스트 통과 불가
-
// MemberService
public int getMemberCount() {
int count = 0;
SqlSession session = getSession(); // connection 객체라고 생각하면 됨
count = dao.getMemberCount(session); // dao에 요청
session.close();
return count;
// 위 코드 작성 전에 0으로 테스트 진행
return 0;
}
MemberService에 test에서 사용했던 메서드도 생성해준다.
이렇게 테스트 코드를 작성한 뒤에..
테스트가 통과될 수 있게 비즈니스 코드를 리팩토링 한다고 한다..!
뭔지는 알겠지만 아직은 왜 이렇게 하는지 잘 이해는 안 되고..
적응도 안 된다.ㅎㅎ..
우리가 예측을 하면서 코딩한다고??
-
// MemberDao
public class MemberDao {
public int getMemberCount(SqlSession session) {
return 0;
}
}
dao에서 return값을 service에 넘겨주고
service는 이걸 count 변수에 담아준다.
이건 servlet/JSP 수업 때 MVC2 패턴 사용했던 거랑 동일하다.
다른 건 SqlSession 객체를 사용하는 것.
-
// MemberDao
public class MemberDao {
// 회원수 조회
public int getMemberCount(SqlSession session) {
return session.selectOne("memberMapper.selectCount");
}
Dao 파일을 다시 이렇게 수정했다.
SqlSession 객체를 받아서 쿼리문을 연결해준다.
member-mapper에 있는 namespace가 memberMapper인 쿼리를 찾아 연결시켜준다.
<!-- member-mapper.xml -->
<mapper namespace="memberMapper"> <!-- namespace 일치하는 거 먼저 찾고 -->
<select id="selectCount" resultType="_int"> <!-- 일치하는 id를 찾아서 -->
SELECT COUNT(*) FROM MEMBER <!-- 이 쿼리문을 실행시켜 준다!! -->
</select>
</mapper>
기존에 작성했던 Dao 코드랑은 좀 많이 다르다.
일단 쿼리문이 분리되기 때문에 코드가 많이 간결해졌다.
이게 mybatis의 장점인가봐요…!
SqlSession
- 객체 하나를 조회하기 위해 SqlSession 객체의 selectOne() 메서드 사용
- 첫번째 매개값은 쿼리문이 존재하는 경로(member-mapper의 namespace. 쿼리문의 ID)
- mybatis-config에 지정한 mapper 파일만 가능
- 두번째 매개값은 쿼리문에서 사용될 파라미터 객체
- SqlSession 객체는 mybatis-cofig.xml 파일에 DB 연결 정보를 확인 후 등록된 mapper 중 연결된 것을 수행
오늘 수업은 일단 여기까지인데 세미하면서 이제야 servlet/JSP에 좀 익숙해졌나 했더니..
mapper 파일이 또 새로 생기다니.ㅋㅋㅋ 정신 똑바로 차려.