이야기박스
(작성중) Spring Test & JUNIT 본문
수정 중인 문서입니다.
# 스프링 테스트란?
스프링은 복잡한 애플리케이션 개발을 위하여 IoC/DI를 이용하는 객체지향 프로그래밍 프레임워크입니다. IoC/DI를 이용하여 복잡한 애플리케이션을 구현했다고 가정을 해보겠습니다.
이제 서비스 검증을 위하여 테스트를 진행해야 하는데, 애플리케이션이 복잡해서 무엇부터 진행해야 하는지 막막합니다.
이때, 사용되는 것이 스프링 테스트입니다.
# 테스트
## Unit Test (단위 테스트)
단위 테스트는 개발자가 설계하고 만든 코드가 원래 의도한 대로 동작하는지를 개발자 스스로 빨리 확인하기 위하여 구성합니다.
기본적으로 확인 대상(Result)과 조건(Condition)이 간단할수록 좋지만 그렇다고 무조건 단위 테스트를 작게 구성하는 것이 좋다고 생각되지는 않습니다.
너무 많은 단위 테스트는 또다른 관리 요소가 될 수 있기 때문에, 개발자 스스로 판단하여 적절한 선을 지키는 게 좋은 것 같습니다.
## 자동수행 테스트
사람이 직접 테스트를 진행하다 보면 Human error도 발생할 수 있기 때문에 가능하다면 자동화 구성을 해두는 게 좋습니다.
자동화의 필요성
- 수동 확인 작업의 번거로움
- 실행 작업의 번거로움
위 내용들은 아래에서 계속 다루도록 하겠습니다.
추가적으로 최근 CI에 관하여 공부를 하고 있는데, 문서를 첨부합니다. (작성 예정입니다.)
# 테스트 자동화
대부분의 IDE에서는 JUnit을 지원하기 때문에 테스트는 Main 메소드가 아닌 JUnit을 통하여 이루어집니다.
- 테스트 코드 자동화
- 데스트 실행 자동화
이 두 내용을 위주로 다루도록 하겠습니다.
## 테스트 코드 자동화
테스트 결과를 사용자가 매번 확인/검증을 해서는 안됩니다. 결과를 바탕으로 한눈에 보기 좋게 필터링하여 결과를 내주는 테스트 코드 작성을 해야 합니다.
AssertThat
첫 번째 파라미터의 값을 뒤에 나오는 Matcher 조건으로 비교해서 일치하면 다음으로 넘어가고, 아니면 테스트가 실패하도록 만들어줍니다.
테스트 코드에서 결과 확인용으로 사용되는 if 구문은 assertThat으로 바꿔주면 좋을 것 같습니다.
AssertThat(param1, param2)
-------------- 여기서부터 이어서 작성
테스트 구성
로드 존슨(스프링 창시자)
"항상 네거티브 테스트를 먼저 만들라"
실패 케이스를 하나씩 소거해나가면, 실 서비스에서는 보다 안전한 서비스가 되겠죠?
실패 테스트를 작성하기 위하여 다음과 같은 작업을 하면 좋습니다.
# Exception이 발생하지 않으면 실패한 테스트 만들기
@Test(expected=oooException.class)
# Reference
이전 문서
[테스트 코드를 사용하는 경우 문제점 2가지]
- 검증은 셀프
- 테스트 결과를 어느정도 확인할 수 있도록 한번 필터링한 코드 사용하자 (ex/ if문 )
- 최종 테스트 결과 메시지를 먼저 확인하자 (성공/실패 여부)
- 테스트 코드 실행도 반복되면 귀찮다
JUnit 사용할 것
- public 메소드
- @Test 어노테이션
* IDE에서는 JUnit 테스트 지원도구를 가지고 있음
* addAndGet() 처럼 테스트 마치고 테스트가 등록한 정보를 삭제하여 초기화해주는 프로세스가 있으면 좋음
ex/ deleteAll(), getCount()..
테스트 코드의 요소
- 조건
- 행위
- 결과
==> 테스트 설계가 프로젝트 설계의 일부분이 될 수 있음
TDD (Test Driven Development)
테스트 코드를 먼저 만들고 테스트를 성공하게 해주는 코드를 작성하는 방식의 개발 방법
TFD(Test First Development)라고 불리기도 함
장점
코드 생성 - 테스트 실행 사이의 간격이 짧음
테스트 코드 개선
@Before 어노테이션
중복됐던 코드를 밖으로 빼주는 Junit 기능
@After 어노테이션
@Test 코드 실행 이후 실행
Junit 실행 순서
- 테스트 클래스에서 @Test 어노테이션이 붙고 public인 메서드를 모두 찾음
- 테스트 클래스의 오브젝트를 하나 만듬
- @Before 붙은 메서드 있으면 먼저 실행
- @Test 붙은 메서드를 하나 호출 & 테스트 결과 저장
- @After 붙은 메서드 있으면 실행
- 남은 @Test 메서드에 대하여 2~5번 반복
- 결과 종합하여 보여줌
* 테스트 오브젝트를 매번 만드는 이유, 각 테스트가 독립적으로 동작하는 것을 보장하기 위하여
* 픽스처 (Fixture)
테스트를 수행하는데 필요한 정보나 오브젝트
보통 @Before를 통하여 생성함
Spring Junit maven
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "/applicationContext.xml")
public class UserDaoTest {
// SpringJUnit에서 context를 클래스 실행전에 만들어 줌
@Autowired
private Application context;
@Before
public void setUp() {}
}
@Autowired
변수 타입과 일치하는 컨텍스트 내의 빈을 찾음 --> 일치하는 빈이 있으면 인스턴스 변수에 주입
* ApplicationContext는 초기화할 때, 자기 자신도 빈으로 등록함
--> @Autowired가 먹힘
DI시 사용할 시, 인터페이스 vs 구현 클래스 어떻게 할지 고민할 때
오브젝트 자체에 관심이 있다면, 구현클래스가 좋은 선택이 될 수 있음
하지만 그렇지 않다면 인터페이스 사용을 지향합시다!
인터페이스를 두고 DI를 넣어야 하는 이유
1. 소프트웨어 개발에서 절대로 바뀌지 않는 것은 없음
2. 다른 차원의 서비스 기능 도입 (?)
3. 테스트 쉬움
[인터페이스 DI의 테스트 예시]
// @DirtiesContext 어플리케이션 컨텍스트의 구성이나 상태를 변경할 수 있음을 테스트 컨텍스트 프레임워크에 전달
@DirtiesContext
public class InterfaceDITest {
@Autowired
UserDao dao;
@Before
public void setUp() {
DataSource dataSource = null;
// datasource 선언
// ..
// 코드를 통한 수동 DI
dao.setDataSource(dataSource);
}
}
--> xml 수정하지 않고도 오브젝트 관계 수정 가능
@DirtiesContext --> 클래스뿐이 아니라 메서드에도 사용 가능..
찜찜하면 test-applicationContext.xml 만들어서 사용할 것
아니면, DI 없이 그냥 선언해서 사용해도 됨 (@Autowired 안쓰고)
DI는 객체지향 프로그래밍 스타일. DI를 위해 컨테이너가 반드시 필요한 것은 아님.
학습 테스트
public class JUnitTest {
static JUnitTest testObject;
@Test
public void test1() {
assertThat(this, is(not(sameInstance(testObject))));
testObject = this;
}
@Test
public void test2() {
assertThat(this, is(not(sameInstance(testObject))));
testObject = this;
}
@Test
public void test3() {
assertThat(this, is(not(sameInstance(testObject))));
testObject = this;
}
}
버그 테스트
'Programming Language > Spring' 카테고리의 다른 글
JPA. generate ddl-auto alphabetical order (4) | 2022.09.09 |
---|---|
(작성중) Spring. HttpMessageConverters (0) | 2019.11.28 |
Spring. SpringJDBC - ibatis 환경에서 Transaction 진행 (1) | 2019.10.18 |
Spring - Filter (0) | 2019.08.13 |
Spring JPA. Batch Insert 에러 (0) | 2019.03.28 |