2.3.5 테스트 코드 개선

JUnit 프레임워크를 이용해서 테스트 코드를 진행한다.

JUnit이 제공하는 기능 중 테스트를 실행할 때마다 반복되는 준비 작업을 별도의 메소드로 빼서 매번 테스트 메소드를 실행하기 전과 후에 실행시켜줄 수 있는 기능이 존재한다.

@Before

@Before 애너테이션은 테스트 메소드 실행 전에 먼저 실행되는 기능이다.

매번 테스트 코드를 실행하면서 필요한 변수들을 중복 제거하기 위해서 사용한다.

테스트 메소드에서 객체를 사용해야 되는 경우

클래스의 인스턴스 변수로 선언을 먼저 한 뒤 @Before 애너테이션이 붙은 메서드에서 객체를 생성해 주는 방식으로 사용한다.

인스턴스 변수를 이용하는 이유는 @Before, @After 메소드를 테스트메소드에서 직접 호출하지 않기 때문이다.

private UserDao dao;

@Before
public void setUp() {
	ApplicationContext context =
			new GenericXmlApplicationContext("applicationContext.xml");
	this.dao = context.getBean("userDao", UserDao.class);
}

Junit이 테스트를 수행하는 7단계 방식

  1. 테스트 클래스에서 @Test가 붙은 public이고 void형이며 파라미터가 없는 테스트 메소드를 모두 찾는다.
  2. 테스트 클래스의 오브젝트를 하나 만든다.
  3. @Before가 붙은 메소드가 있으면 실행한다.
  4. @Test가 붙은 메소드를 하나 호출하고 테스트 결과를 저장해둔다.
  5. @After가 붙은 메소드가 있으면 실행한다.
  6. 나머지 테스트 메소드에 대해 2~5번을 반복한다.
  7. 모든 테스트의 결과를 종합해서 돌려준다.

기억해야 할 사항

각 테스트 메소드를 실행할 때마다 테스트 클래스의 오브젝트를 새로 만든다.

각 테스트가 서로 영향을 주지 않고 독립적으로 실행됨을 확실히 보장해 주기 위해서 매번 새로운 오브젝트를 만드는 것이다.

픽스처(fixture)

픽스처란? 테스트를 수행하는 데 필요한 정보나 오브젝트를 뜻한다.

UserDaoTest를 예시로 들면 dao가 대표적인 픽스처로 볼 수 있다. 테스트 중 add() 메소드에 전달하는 User 오브젝트들도 픽스처라고 볼 수 있다.

UserDao에 대한 테스트라면 대부분 User 오브젝트를 사용할 것으로 예상된다. UserDao의 기능이 계속 만들어지고 그에 따라 테스트 메소드도 계속 추가될 수 있기 때문이다.

픽스처 생성 로직은 흩어져있는 것보다 모여 있는 편이 낫기 때문에 @Before 메소드를 이용한다.

private UserDao dao;
private User user1;
private User user2;
private User user3;

@Before
public void setUp() {
	...
	this.user1 = new User("gyumee", "박성철", "springno1");
	this.user2 = new User("leegw700", "이길원", "springno2");
	this.user3 = new User("bumjin", "박범진", "springno3");

2.4 스프링 테스트 적용

위처럼 테스트 코드를 작성하는 경우 @Before 메소드가 테스트 메소드 개수만큼 반복되기 때문에 애플리케이션 컨텍스트도 세 번 만들어진다. 빈이 많아지고 복잡해진다면 애플리케이션 컨텍스트 생성에 적지 않은 시간이 걸릴 수 있다.

애플리케이션 컨텍스트가 만들어질 때 모든 싱글톤 빈 오브젝트를 초기화한다.

그러나 어떤 빈은 오브젝트가 생성될 때 자체적인 초기화 작업을 진행해서 많은 시간을 필요로하거나 애플리케이션 컨텍스트가 초기화될 때 독자적으로 많은 리소스를 할당하거나 독립적인 스레드를 띄우는 문제점이 있다. 이런 경우 테스트를 마칠 때마다 애플리케이션 컨텍스트 내의 빈이 할당한 리소스 등을 깔끔하게 정리해줘야한다.

테스트는 일관성 있는 실행 결과를 보장해야 하고, 테스트의 실행 순서가 결과에 영향을 미치지 않아야 한다.

2.4.1 테스트를 위한 애플리케이션 컨텍스트 관리

스프링 테스트 컨텍스트 프레임워크 적용

애플리케이션 컨텍스트는 한 번만 만들고 여러 테스트가 공유해서 사용해도 되기 때문에 @Before 메소드에서 애플리케이션 컨텍스트를 생성하는 코드를 제거해도 된다.

수정 전

@Before
public void setUp() {
	ApplicationContext context =
			new GenericXmlApplicationContext("applicationContext.xml");
	this.dao = context.getBean("userDao", UserDao.class);
}

수정 후

@Runwith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="/applicationContext.xml")
// 톰캣 뜰 때 리스너가 뜨는 거랑 비슷하게 작동
public class UserDaoTest {
	@Autowired
	private ApplicationContext context; // 싱글톤
	
	...
	
	@Before
	public void setUp() {
		this.dao = this.context.getBean("userDao", UserDao.class);
	}
}

인스턴스 변수인 context는 초기화 해주는 코드가 없지만 애플리케이션 컨텍스트가 들어가 있기 때문에 문제 없이 성공적으로 끝난다.

테스트 메소드의 컨텍스트 공유

직접 확인해보기위해 코드를 추가한다.

	@Before
	public void setUp() {
		System.out.println(this.context);
		System.out.println(this);
	}

실행 결과는 다음과 같다.

org.springframework.context.support.GenericApplicationContext**@d3d6f:**
springbook.dao.UserDaoTest**@115d06c**
org.springframework.context.support.GenericApplicationContext**@d3d6f:**
springbook.dao.UserDaoTest**@116318b**
org.springframework.context.support.GenericApplicationContext**@d3d6f:**
springbook.dao.UserDaoTest**@15e0c2b**

출력된 결과로 보았을 때 context는 세 번 모두 동일하게 나왔다. 하나의 애플리케이션 컨텍스트가 모든 테스트 메소드에서 사용되고 있음을 알 수 있다. 싱글톤 패턴이 적용된 것을 알 수 있다.

반면에 UserDaoTest의 오브젝트는 매번 주소 값이 다르다. JUnit은 테스트 메소드를 실행할 때마다 새로운 테스트 오브젝트를 만들기 때문이다.

@Autowired

@Autowired는 “주입 받는 것” 이다.(DI : 의존성 주입)

주입받는 다는 것은 자주 바뀌는 부분(n개)을 따로 빼서 매개변수로 받아오는 것으로, 기능과 구현을 분리한 것을 뜻한다.

2.5 학습 테스트로 배우는 스프링

학습 테스트?

예시 1

  • MyVector 테스트를 먼저 해보면 Vector 사용법을 알 수 있다.

예시 2

  • spring 6.xx로 돌리던 것을 spring 7.xx로 돌리면 어떤게 달라졌는지 알 수 있다.

2.5.1 학습 테스트의 장점

  • 다양한 조건에 따른 기능을 손쉽게 확인해볼 수 있다
  • 학습 테스트 코드를 개발 중에 참고할 수 있다.
  • 프레임워크나 제품을 업그레이드할 때 호환성 검증을 도와준다.
  • 테스트 작성에 대한 좋은 훈련이 된다.
  • 새로운 기술을 공부하는 과정이 즐거워진다.

2.6 정리

2장에서는 테스트의 필요성과 작성 방법에 대하여 다루었다.

  • 테스트는 자동화돼야 하고, 빠르게 실행할 수 있어야 한다.
  • main() 테스트 대신 JUnit 프레임워크를 이용한 테스트 작성이 편리하다.
  • 테스트 결과는 일관성이있어야 한다.
    코드의 변경 없이 환경이나 테스트 실행 순서에 따라서 결과가 달라지면 안된다.
  • 테스트는 포괄적으로 작성해야 한다.충분한 검증ㅇ르 하지 않는 테스트는 없는 것보다 나쁠 수 있다.
  • 코드 작성과 테스트 수행의 간격이 짧을 수록 효과적이다.
  • 테스트하기 쉬운 코드가 좋은 코드다.
  • 테스트를 먼저 만들고 테스트를 성공시키는 코드를 만들어가는 테스트 주도 개발 방법(TDD)도 유용하다.
  • 테스트 코드도 애플리케이션 코드와 마찬가지로 적절한 리팩토링이 필요하다.
  • @Before, @After를 사용해서 테스트 메소드들의 공통 준비 작업과 정리 작업을 처리할 수 있다.
  • 스프링 테스트 컨텍스트 프레임워크를 이용하면 테스트 성능을 향상시킬 수 있다.
  • 동일한 설정파일을 사용하는 테스트는 하나의 애플리케이션 컨텍스트를 공유한다.
  • @Autowired를 사용하면 컨텍스트의 빈을 테스트 오브젝트에 DI 할 수 있다.
  • 기술의 사용 방법을 익히고 이해를 돕기 위해 학습 테스트를 작성하자.
  • 오류가 발견될 경우 그에 대한 버그 테스트를 만들어두면 유용하다.
profile
개발하는 다람쥐

0개의 댓글