public class UserDaoTest {
private UserDao dao;
@Before
public void setUp() {
ApplicationContext context = new GenericXmlApplicationContext("applicationContext.xml");
this.dao = context.getBean("userDao", UserDao.class);
}
@Test
public void addAndGet() throws SQLException {
...
}
@Test
public void count() throws SQLException {
...
}
@Test(expected=EmptyResultDataAccessException.class)
public void getUserFailure() throws SQLException {
...
}
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "/applicationContext.xml")
public class UserDaoTest {
@Autowired
private ApplicationContext context;
@Before
public void setUp() {
this.dao = context.getBean("userDao", UserDao.class);
}
...
}
@Before
public void setUp() {
System.out.println(this.context);
System.out.println(this);
...
}
org.springframework.web.context.support.GenericWebApplicationContext@157fb63f
com.tobyspring.dao.UserDaoTest@36151fdb
org.springframework.web.context.support.GenericWebApplicationContext@157fb63f
com.tobyspring.dao.UserDaoTest@27a82eae
org.springframework.web.context.support.GenericWebApplicationContext@157fb63f
com.tobyspring.dao.UserDaoTest@5aabb5c3
타입에 의한 자동 와이어링
이라한다.public class UserDaoTest {
@Autowired
UserDao userDao;
...
}
어플리케이션 컨텍스트가 가지고 있는 빈을 DI 받을 수 있다면 굳이 getBean()을 이용하지 않고 UserDao 빈을 받을 수 있다.
항상 SimpleDriverDataSource를 쓸 예정인데 굳이 Datasource 인터페이스를 사용하고 DI를 통해 주입받는 방식을 쓸 필요가 있을까?
그래도 인터페이스를 두고 DI를 적용해야 한다.
1. 소프트웨어 개발에서 절대로 바뀌지 않는 것은 없기 때문이다.
2. 클래스의 구현 방식은 바뀌지 않는다고 하더라도 인터페이스를 두고 DI를 적용하게 해두면 다른 차원의 서비스 기능을 도입할 수 있기 때문이다.
- 1장에서 만든 DB 커넥션 개수를 카운팅 하는 부가기능이 그런 예다.
3. 테스트 때문이다.
- DI는 테스트가 작은 단위의 대상에 대해 독립적으로 만들어지고 실행되게 하는데 중요한 역할을 한다.
DI를 이용해 테스트 중 DAO가 사용하는 DataSource 구현 클래스를 바꿔주는 방법으로 운영 DB와 테스트 DB를 분리할 수 있다.
@DirtiesContext
public class UserDaoTest {
@Autowired
UserDao userDao;
@Before
public void setUp() {
...
DataSource dataSource = new SingleConnectionDataSource(
"jdbc:mysql://localhost/testdb", "spring", "book", true
);
userDao.setDataSource(dataSource);
}
}
다른 테스트에서도 변경된 애플리케이션 컨텍스트를 사용하게 된다.
이는 별로 바람직하지 않다. 그래서 @DirtiesContext
어노테이션을 추가해 변경된 컨텍스트를 공유하지 않도록 할 수 있다.테스트 코드에서 빈 오브젝트를 수동으로 DI 하는 방법은 장점보다 단점이 많다. 코드가 많아져 번거롭기도 하고 애플리케이션 컨텍스트를 매번 만들어야 하는 부담이 있다.
이 문제는 applicationContext.xml 을 복사해 text-applicationContext.xml 을 만들어 운영으로 사용할 DataSource 와 테스트에 적합하게 준비된 DB를 사용하는 가벼운 DataSource 를 빈으로 등록하여 테스트에서는 항상 테스트 전용 설정파일만 사용하게 해줌으로써 해결할 수 있다.
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "/test-applicationContext.xml")
public class UserDaoTest {
...
}
이로써 번거롭게 수동 DI 하는 코드나 @DirtiesContext 가 필요 없어졌다.
public class UserDaoTest {
UserDao userDao;
@Before
public void setUp() {
...
dao = new UserDao();
DataSource dataSource = new SingleConnectionDataSource(
"jdbc:mysql://localhost/testdb", "spring", "book", true
);
userDao.setDataSource(dataSource);
}
}
DataSource 를 직접 만들어야 하지만 애플리케이션 컨텍스트를 아예 사용하지 않으니 코드는 더 단순해지고 이해하기 쉬워졌다. 그리고 테스트 시간도 짧아졌다.
학습테스트란 자신이 만들지 않은 프레임워크나 다른 개발팀에서 만들어서 제공한 라이브러리 등에 대한 테스트이다.
테스트 메소드에서 매번 동일한 application context 가 주입 됐는지 확인하는 테스트
@SpringBootTest
public class JUnitTest {
@Autowired
ApplicationContext applicationContext;
static Set<JUnitTest> testObjects = new HashSet<>();
static ApplicationContext contextObject = null;
@Test
void test1() {
assertThat(testObjects).doesNotContain(this);
testObjects.add(this);
assertThat(contextObject == null || contextObject == this.applicationContext).isTrue();
contextObject = this.applicationContext;
}
@Test
void test2() {
assertThat(testObjects).doesNotContain(this);
testObjects.add(this);
assertTrue(contextObject == null || contextObject == this.applicationContext);
contextObject = this.applicationContext;
}
@Test
void test3() {
assertThat(testObjects).doesNotContain(this);
testObjects.add(this);
MatcherAssert.assertThat(contextObject, either(is(nullValue())).or(is(this.applicationContext)));
contextObject = this.applicationContext;
}
}
코드에 오류가 있을 때 그 오류를 가장 잘 드러내줄 수 있는 테스트, 일단 버그가 발생하는 조건의 테스트를 만들고 테스트가 성공하면 버그가 해결되게 한다.
동등분할(equivalence partitioning)
같은 결과를 내는 값의 범위를 구분해서 각 대표 값으로 테스트하는 방법을 말한다. 어떤 작업의 결과의 종류가 true, false 또는 예외발생 세 가지라면 각 결과를 내는 입력 값이나 상황의 조합을 만들어 모든 경우에 대한 테스트를 해보는 것이 좋다.
경계값 분석(boundary value analysis)
에러는 동등분할 범위의 경계에서 주로 많이 발생한다는 특징을 이용해서 경계의 근처에 있는 값을 이용해 테스트하는 방법이다. 보통 숫자의 입력 값인 경우 0이나 그 주변 값 또는 정수의 최대값, 최소값 등으로 테스트해보면 도움이 될 때가 많다.