테스트-1

정훈희·2022년 10월 15일
0

Spring

목록 보기
7/24
post-thumbnail

테스트-1

참조

  • 토비의 스프링 3.1 vol.1 145p~168p

보통 웹에서 사용하는 DAO를 테스트하는 방법은 다음과같다.

  • DAO를 만들고, 서비스 계층, MVC 프레젠테이션 계층까지 포함한 모든 입출력 기능을 대충이라도 다 코드로 만든다
  • 이렇게 만들어진 테스트용 웹 애플리케이션을 서버에 배치한뒤 웹 화면을 띄워 폼을 열고 값을 입력한 뒤 버튼을 눌러 등록해본다.
  • 파라미터를 지정할 수 있는 URL을 사용해서 방금 입력한 데이터를 다시 가져올 수 있는지 테스트해본다.

이렇게 웹을 통해 값을 입력하고, 기능을 수행하고, 결과를 확인하는 방법은 가장 흔히 쓰이지만, DAO에 대한 테스트로써는 단점이 너무 많다.

DAO뿐만 아니라 서비스, 컨트롤러, JSP뷰 등 모든 기능을 다 만들고 나서야 테스트가 가능하다는 점이 문제이다.

게다가 테스트를 하는 중에 에러가 나거나 테스트가 실패했다면, 어디서 문제가 발생했는지를 찾기도 힘들다.

→ 단위 테스트 필요


단위 테스트(unit test)

테스트하고자 하는 대상이 명확하다면 그 대상에만 집중하는 것이 좋다. 관심사가 다르다면 테스트할 대상을 분리하고 집중해서 접근해야 한다.

자동수행 테스트 코드

테스트를 위해 매번 웹화면을 띄우고, 테스트용 값을 직접 입력하고, 버튼을누르고 하려면 상당히 번거롭다. 그러므로 테스트는 자동으로 수행되도록 코드로 만들어 지는것이 중요하다.

참고로 애플리케이션을 구성하는 클래스 안에 테스트 코드를 포함시키는 것보다는 별도의 테스트용 클래스를 만들어서 테스트 코드를 넣는 편이 낫다.

UserDaoTest의 문제점

UserDaoTest는 확실히 수동 테스트에 비해 장점이 많지만 아래와 같은 단점이 있다.

  • 수동 확인의 번거로움 add 메소드로 DB에 정보를 등록하고, 다시 get 메소드로 가져오지만 입력한 값과 가져온 값이 일치하는지는 수동으로 확인해야한다.
  • 실행 작업의 번거로움 만약 DAO가 수백개가 되고, 그에 대한 main 메소드들이 만들어지면 전체 기능을 테스트해보기 위해서 main을 수백번 실행해야한다.

테스트 검증의 자동화

위의 문제점 중 수동 확인의 번거로움을 해결해보자.

System.out.println(user2.getName());
System.out.println(user2.getPassword());
System.out .println(user2 .getld() + " 조회 성공");

위 코드는 기존 UserDaoTest의 코드이다.

if (!user.getName().equals(user2.getName())) {
	System.out .println("테스트 실패 (name)");
}
else if (!user.getPassword().equals(user2.getPassword())) (
	System.out.println("테스트 실패 (password)");
}
else (
	System.out.println("조회 테스트 성공’);
}

위 코드는 테스트 검증을 자동화 하도록 개선한 코드이다.


JUnit

JUnit은 프레임 워크이다. 즉 JUnit으로 테스트를 진행 하면 개발자가 직접 오브젝트를 생성하고 실행할 필요가 없다. JUnit은 두 가지 조건을 요구한다. 일단, 메소드가 public으로 선언돼야 하고, 메소드에 @Test 어노테이션을 붙여주는 것이다.

UserDaoTest를 JUnit 테스트로 바꿔보면

import org.junit.Test;
// ...
public class UserDaoTest (
	@Test
	public void addAndGet() throws SQLException {
		ApplicationContext context = new
			ClassPathXmlApplicationContext( "applicationContext.xml");
		UserDao dao =context.getBean("userDao", UserDao.class);
		// ...
	}
}

이렇게 된다. 보면 @Test 어노테이션을 붙혔고, 메소드를 public으로 선언했다.

이제 검증 코드를 JUnit이 제공하는 방법으로 바꿔보자.

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
public class UserDaoTest {
	@Test
	public void addAndGet() throws SQLException {
		ApplicationContext context =new GenericXmlApplicationContext(
			"applicationContext.xml");
		UserDao userDao = context.getBean("userDao", UserDao.class);
		User user = new User();
		user.setld("gyumee");
		user.setName("박성철");
		user.setPassword("springnol");
		dao.add(user);
		User user2 =dao.get(user.getld());
		assertThat(user2.getName(), is(user.getName()));
		assertThat(user2.getPassword(), is(user.getPassword()));
	}
}

assertThat은 첫 번째 파라미터의 값을 뒤에 나오는 matcher라고 불리는 조건으로 비교해서 일치하면 다음으로 넘어가고, 아니면 테스트가 실패하도록 만들어준다. is() matcher의 일종으로 equals()로 비교해주는 기능을 가졌다.

JUnit은 예외가 발생하거나 assertThat()에서 실패하지 않고 테스트 메소드의 실행이 완료되면 테스트가 성공했다고 알려준다.


테스트 결과의 일관성

현재 만든 테스트는 매번 테스트 실행 전에 DB의 User 테이블 데이터를 모두 삭제해줘야 한다. 여기서 생각해볼 문제는 테스트가 외부 상태에 따라 성공하기도 하고 실패하기도 한다는 점이다.

이 문제를 해결하려면 테스트를 마친 뒤에 테스트가 등록한 정보를 삭제하여 테스트 이전 상태로 만들어주면 된다. 그렇게 되면 테스트를 여러번 반복하더라도 항상 동일한 결과를 얻을 수 있다.

일단 UserDao에 deleteAll() 메소드와 getCount() 메소드를 추가해보자.

public void deleteAll() throws SQLException {
	Connection c = dataSource.getConnection();
	PreparedStatement ps = c.prepareStatement(“delete from users");
	ps.executeUpdate();
	ps.close();
	c.close();
}

public int getCount() throws SQLException {
	Connection c =dataSource.getConnection();
	PreparedStatement ps =c.prepareStatement("select count(*) from users");
	ResultSet rs = ps.executeQuery();
	rs.next();
	int count = rs.getlnt(1);
	rs.close();
	ps.close();
	c.close();
	return count;
}

deleteAll() 메소드는 User테이블의 모든 레코드를 삭제하고, getCount() 메소드는 User테이블의 레코드 개수를 반환한다.

이제 새로 추가된 두 메소드의 테스트 코드를 작성해보자.

@Test
public void addAndGet() throws SQLException {
	// ...
	dao.deleteAll();
	assertThat(dao.getCount(), is(0));
	User user = new User();
	user.setId("gyumee");
	user.setName("박성철");
	user.setPassword("springnol");
	dao.add(user);
	assertThat(dao.getCount(), is(1));
	User user2 = dao.get(user.getld());
	assertThat(user2.getName(), is(user.getName()));
	assertThat(user2.getPassword(), is(user .getPassword()));
}

테스트 시작시 남아있는 데이터를 지우고, getCount메소드로 남은 레코드 수가 0인지 확인한다. 이후 기존 처럼 새로운 user 데이터를 추가하고, 추가한 뒤 getCount로 남은 레코드 수가 1인지 확인한다.

단위 테스트는 이처럼 항상 일관성 잇는 결과가 보장되어야한다. DB에 남아있는 데이터와 외부환경에 영향을 받지 말아야 하는 것은 물론이고, 테스트를 실행하는 순서를 바꿔도 동일한 결과가 보장되도록 만들어야 한다.

profile
DB를 사랑하는 백엔드 개발자입니다. 열심히 공부하고 열심히 기록합니다.

0개의 댓글