본문 바로가기
Spring/개념

02장 Test

by clearinging 2021. 4. 20.
반응형

테스트 코드를 작성해야하는 이유

  • 하나의 로직을 테스트 하기위해서 JSP와 같은 view를 작성해야함
  • view단에서 발생하는에러인지 아님 비즈니스 로직으로 발생하는 에러인지 판단하기 힘듦
  • 서버 환경으로 발생하는 문제까지 고려 해야하는 추가 노동이 필요

Unit 테스트

  1. 정의: 하나의 대상을 집중해서 작은 단위로 테스트를 진행하는 것
  2. 특징
  • 테스트와 관심사를 분리
  • view(JSP, HTML)를 추가 작업의 노동을 절약
  • 의도한 대로 동작하는지 개발자가 빠르게 피드백 받을 수있음
  • 자동화를 해주면 자주 반복해서 사용할 수 있음

테스트 자동화의

  1. 실패하는 경우 2가지 고려
  • 테스트 진행하는 동안 에러발생(런타임 에러)
  • 에러가 발생하지 않고 얘상한 값과 실제 값이 다른 경우
  1. 과거 main문을 활용한 테스트
public class UserDaoTest {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        System.out.println("UserDao 테스트 시작");

        ApplicationContext context = new GenericXmlApplicationContext("/applicationContext.xml");
        UserDao dao = context.getBean("userDao", UserDao.class);

        User user = new User("username", "password"); // username, password
        User user2 = dao.add(user);
                if (!user.getPassword().equals(user2.getPassword())) {
                        System.out.println("테스트 실패 : password");
                } else if (!user.getUsername().equals(user2.getUsername())) {
                        System.out.println("테스트 실패 : username");
                } else {
                        System.out.println("테스트 성공");
                }
    }
}
  1. 솔루션 : JUnit

Junit

Intellij 단축키 : mac os - ctrl + space, windows - ctrl + shift + t

  1. 정의 : Java에서 지원하는 테스트 전용 프레임 워크
  2. 특징
  • Junit 4 까지public 형태로 정의 해야함 → 5.0부터는 안해도 됩니다.
  • method 마다 @Test 애노테이션을 추가해야 합니다.
  • 매번 독립적인 객체를 만드므로, 각 테스트가 독립적인 환경임을 보장해줘서 side effect를 방지할 수 있음
  1. 실행 순서
    1) @Test, public void, 파라미터가 없는 메소드 검색
    2) 테스트 대상이 되는 클래스 Object를 하나 만든다
    3) @Before 메소드가 있다면 실행
    4) @Test메소드를 하나 실행하고 결과를 저장
    5) @After 메소가 있다면 실행
    6) 모든 @Test메소드에 대해 2~5 단계 실행 반복
    7) 테스트 결과를 취합해 반환
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);
                User user = new User(); 
                user. setld ( “gyumee") ; 
                user. setName(" 박성 철 "); 
                user.setPassword("springnol"); 

                dao.add(user);
                assertThat(user2.getName(), is(user.getName())); 
                assertThat(user2.getPassword(), is(user.getPassword()));
        }
}
  1. 예외 경우 테스트
import org.junit.Test;

public class UserDaoTest (
        @Test(expected=EmptyResultDataAccessException.class)
        public void getUserFailure() throws SQLException {
                ApplicationContext context = new 
                    ClassPathXmlApplicationContext("applicationContext.xml");
                UserDao dao = context.getBean("userDao" , UserDao.class);
                dao.deleteAll();
                assertThat(dao.getCount(), is(0)); 
                dao.get("unknown_id"); // exception 발생 예상 지점
        }
}
  1. 테스트코드 구조
  단계 내용 코드
조건 어떤 조건을 가지고 가져올 사용자 정보 존재하지 않는 경우 dao.deleteAll(); asssertThat(dao.getCount(), is(0);
행위 무엇을 할 때 존재하지 않는 id로 get()을 실행 get("unknown_id");
결과 어떤 결과가 나온다 특별한 예외 던진다 @Test(expected=EmptyResultDataAccessException.class)

Context와 Bean의 재사용

  • ApplicationContext는 초기화되고 공유 가능한 요소
  • Bean은 Singleton이기 때문에 공유 가능(stateless)
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "/applicationContext.xml")
public class UserDaoTest {
    @Autowired
    private ApplicationContext context; // bean 주입
    // ...
}
  • @RunWith(SpringJUnit4ClassRunner.class) : 테스트 컨텍스트 프레임워크의 확장 JUnit확장기능 지정 - 테스트에서 사용할 Application Context를 만들고 관리
  • @ContextConfiguration(locations = "/applicationContext.xml") : 어떤 컨텍스트 설정 파일 위치 명시 - 해당 container를 공유 되고 있는 정보

테스트를 위한 수동 DI 적용

//...
@DirtiesContext 
public class UserDaoTest ( 
        @Autowired 
        UserDao dao; 

        @Before
        public void setUp() ( 
                DataSource dataSource = new SingleConnectionDataSource( 
                        "jdbc:mysql:lllocalhost/testdb" , "spring" ’ "book" , true); 
                dao.setDataSource(dataSource);
        }
}
  1. 목적: 의도적으로 기존의 DI를 사용하지 않고 새로 테스트를 위한 object를 생성하고 싶을때 사용
  2. 애노테이션
    • @DirtiesContext : 테스트 메소드에서 애플리케이션 컨텍스트의 구성이나 상태를 변경한다는 것을 테스트 컨텍스트 프레임워크에 알려주는 애노테이션
  3. 장점
  • xml 수정없이 테스트 코드를 통해 object 관계 재구성
  1. 단점
  • 성능 저하 발생 : ApplicationContext를 공유하지 못하기 때문에 성능 이슈 발생
  1. 해결 방안 : 테스트를 위한 xml파일을새로 만들기

테스트 코드 작성 방식

  1. DI를 사용하지 않고 객체를 생성해서 테스트 진행하기
  2. DI를 무조건 사용해야하는경우 DI사용해서 작성하기
  3. 테스트 코드에서 특별한 상황이 발생해서 DI의 객체를 변경이 필요하면 @DirtiesContext 사용하기

TDD

  • 정의 : 테스트 코드 먼저 만들고 개발 진행하는 것

학습용 테스트(Learning Test)

  1. 정의 : 자신이 만들지 않은 라이브러리 프레임 워크를 테스트 하고, 학습을 목적으로 만드는 테스트 코드
  2. 목적과 정점
  • 다양한 조건에 따른 기능을 손쉽게 자동화하여 확인 가능
  • 학습용 테스트 코드 작성 가능
  • 라이브러리 호환등을 점검 가능
  • 테스트 코드 작성 훈련 가능

버그 테스트

  1. 정의 : 코드에 오류가 있을 때 오류를 가장 잘 드러내는 테스트를 만드는 것이다 → 해당 테스트 코드를 통과하면 오류 해결
  2. 장점
    • 테스트 완성도를 높여준다 : 새로우 문제가 발생하더라도 이 테스트 코드를 통해 추적 가능
    • 버그의 내용을 명확하게 분석하게 해준다 : 어떤 문제가 있는지 새로 식별 가능 → 경계값, 동등 분할당 값의 범위 설정해서 테스트 하기
    • 기술적인 문제를 해결하는데 도움이 된다 : 원인 파악이 안될 경우 이슈를 포럼과 공식 문서를 통해서 학습하고 실패 원인 분석할 수 있음
반응형

'Spring > 개념' 카테고리의 다른 글

Spring Test Mock 사용법 및 특징  (1) 2021.10.30
Spring Boot로 MySQL replication 연동하기  (0) 2021.10.06
04 Exception  (0) 2021.05.19
03 템플릿  (0) 2021.05.08
01 장 오브젝트와 의존관계  (0) 2021.05.05