반응형
Jake Seo
제이크서 위키 블로그
Jake Seo
전체 방문자
오늘
어제
  • 분류 전체보기 (715)
    • 일상, 일기 (0)
    • 백준 문제풀이 (1)
    • 릿코드 문제풀이 (2)
    • 알고리즘 이론 (10)
      • 기본 이론 (2)
      • 배열과 문자열 (8)
    • 데이터베이스 (15)
      • Planet Scale (1)
      • MSSQL (9)
      • 디비 기본 개념 (1)
      • SQLite 직접 만들어보기 (4)
    • 보안 (7)
    • 설계 (1)
    • 네트워크 (17)
      • HTTP (9)
      • OSI Layers (5)
    • 회고 (31)
      • 연간 회고 (2)
      • 주간 회고 (29)
    • 인프라 (52)
      • 도커 (12)
      • AWS (9)
      • 용어 (21)
      • 웹 성능 (1)
      • 대규모 서비스를 지탱하는 기술 (9)
    • 깃 (7)
    • 빌드 도구 (7)
      • 메이븐 (6)
      • 그레이들 (0)
    • Java (135)
      • 이펙티브 자바 (73)
      • 자바 API (4)
      • 자바 잡지식 (30)
      • 자바 디자인 패턴 (21)
      • 톰캣 (Tomcat) (7)
    • 프레임워크 (64)
      • next.js (14)
      • 스프링 프레임워크 (28)
      • 토비의 스프링 (6)
      • 스프링 부트 (3)
      • JPA (Java Persistence API) (5)
      • Nest.js (8)
    • 프론트엔드 (48)
      • 다크모드 (1)
      • 노드 패키지 관리 매니저 (3)
      • CSS (19)
      • Web API (11)
      • tailwind-css (1)
      • React (5)
      • React 새 공식문서 요약 (1)
      • HTML (Markup Language) (5)
    • 자바스크립트 (108)
      • 모던 자바스크립트 (31)
      • 개념 (31)
      • 정규표현식 (5)
      • 코드 스니펫 (1)
      • 라이브러리 (6)
      • 인터뷰 (24)
      • 웹개발자를 위한 자바스크립트의 모든 것 (6)
      • 팁 (2)
    • Typescript (49)
    • 리눅스와 유닉스 (10)
    • Computer Science (1)
      • Compiler (1)
    • IDE (3)
      • VSCODE (1)
      • IntelliJ (2)
    • 세미나 & 컨퍼런스 (1)
    • 용어 (개발용어) (16)
      • 함수형 프로그래밍 용어들 (1)
    • ORM (2)
      • Prisma (2)
    • NODEJS (2)
    • cypress (1)
    • 리액트 네이티브 (React Native) (31)
    • 러스트 (Rust) (15)
    • 코틀린 (Kotlin) (4)
      • 자바에서 코틀린으로 (4)
    • 정규표현식 (3)
    • 구글 애널리틱스 (GA) (1)
    • SEO (2)
    • UML (2)
    • 맛탐험 (2)
    • 리팩토링 (1)
    • 서평 (2)
    • 소프트웨어 공학 (18)
      • 테스팅 (16)
      • 개발 프로세스 (1)
    • 교육학 (1)
    • 삶의 지혜, 통찰 (1)
    • Chat GPT (2)
    • 쉘스크립트 (1)
    • 컴파일 (2)
    • Dart (12)
    • 코드팩토리의 플러터 프로그래밍 (4)
    • 플러터 (17)
    • 안드로이드 스튜디오 (1)
    • 윈도우즈 (1)
    • 잡다한 백엔드 지식 (1)
    • 디자인 패턴 (1)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • 메이븐 골
  • 도커공식문서
  • 플라이웨이트패턴
  • prerendering
  • try-with-resources
  • 자바
  • 작업기억공간
  • serverless computing
  • 자바스크립트 면접
  • item9
  • 이펙티브 자바
  • 알고리즘
  • 슬로우 쿼리
  • 서버리스 컴퓨팅
  • 이펙티브자바
  • 빈 검증
  • 참조 해제
  • 이펙티브 자바 item9
  • item8
  • next js app
  • 팩터리 메서드 패턴
  • 추상 팩터리 패턴
  • 스프링 검증
  • 느린 쿼리
  • 싱글톤 패턴
  • 디자인패턴
  • 자료구조
  • NEXT JS
  • Javadoc 자바독 자바주석 주석 Comment
  • 프로그래머의 뇌
  • pnpm
  • 메이븐 페이즈
  • 싱글톤
  • 객체복사
  • 자바스크립트 인터뷰
  • 자바스크립트
  • 토비의 스프링
  • 메이븐 라이프사이클
  • Pre-rendering
  • item7
  • 싱글턴
  • rust
  • Java
  • bean Validation
  • MSSQL
  • 자바 디자인패턴
  • 자바 검증
  • Next.js
  • 외래키 제약조건
  • 러스트

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
Jake Seo

제이크서 위키 블로그

프레임워크/토비의 스프링

토비의 스프링 2장 요약 정리 - 테스트

2021. 12. 26. 16:56

이 포스팅은 2장을 요약 정리한 것이며, 상세한 정리는 이 링크에서 제공한다.

스프링과 테스트

  • 스프링에서 제공하는 가장 중요한 가치 객체지향, 테스트
    • 현대의 앱이 복잡해져가며, 테스트의 중요성은 더욱 높아지고 있다.

좋은 코드는 테스트하기 쉬운 코드라는 특징을 갖는 경우가 많다.

변화에 대응하는 전략

IoC 그리고 DI

  • 스프링의 의존관계 주입형 코드는 로직 코드의 직접적인 수정이 아닌 외부에서 주입하는 객체의 설정정보를 바꾸는 것만으로 손쉬운 변화가 가능하다.

테스트

  • 코드에 변화가 생겼을 때, 해당 코드가 특정 로직에서 정상적으로 작동하는지 알 수 있게 해준다.

테스트 전략

나쁜 테스트의 예 - 웹을 통한 테스트

  • 테스트하고자 하는 코드 외에 부가적인 코드가 너무 많이 들어간다.
  • 모든 레이어의 기능을 다 만들고 나서야 테스트가 가능하다.
  • 다른 코드가 너무 많이 개입하는 경우, 실패의 원인이 해당 테스트코드가 아닐 수도 있다.

좋은 테스트의 예 - 단위 테스트

단위 테스트란 외부에 의존하지 않는 하나의 관심사에 집중하여 테스트하는 것이다.

  • 작은 단위에서 테스트를 진행하면, 어느 부분의 코드가 동작하지 않는지에 대해 상대적으로 명확하다.
  • 외부의 리소스에 의존하지 때문에 통제할 수 있는 범위 내에서 이루어진다.

단위 테스트의 단점

  • 개발을 하다보면 각 단위별로는 잘 작동해도 전체 시스템에서 에러가 나는 경우가 있다.
  • 개발자 입장에서 만들어놓은 테스트기 때문에 고객의 행동은 단위 테스트처럼 천편일률적이지 않다.

그러나 단위테스트가 잘 작성되어 있다면, 통합테스트 시에 발생하는 에러를 빠르게 잡는데도 분명 도움이 된다.

점진적 개발

  • 테스트는 단순히 내가 만든 코드의 기능 확인을 넘어서 지속적 확신을 갖게 해주는 개발 프로세스를 만들어준다.

테스트 검증의 자동화

단순 print문 테스트의 단점

값을 찍어보고 맞는지 눈으로 확인하는 가장 간단한 테스트이다.

  • 사람의 눈으로 값을 확인해야 한다.
    • 사람은 언제나 실수를 할 수 있다. (잘못 볼 수 있다.)
    • 확인해야 할 값이 매우 많을 때는? 사람은 노동력과 시간에 한계가 있다.

결국 컴퓨터가 확인하는 방식으로 전환해야 한다. 이를 위한 프레임워크로 Junit이 있다.

좋은 테스트의 특징

일관성 있는 테스트

  • 테스트 코드에 변경사항이 없다면 외부 영향에 상관없이 동일한 결과를 내는 테스트가 좋은 테스트이다.
  • 어떤 데이터에 의존하는 테스트라면, 테스트 시작 전 데이터를 만들어주고 테스트가 끝난 뒤엔 잘 제거해주면 이러한 일관성을 지켜줄 수 있다.

포괄적인 테스트

  • 테스트를 작성할 때는 꼼꼼하게 작성하는 편이 좋다.
    • 경계값, 다른 타입의 값, null 값, 찾을 수 없는 값 등 다양한 방식으로 테스트해주는 것이 좋다.

위에서 언급한 값들을 테스트하는 것을 네거티브 테스트라고 한다. 이렇게 부정적인 케이스를 먼저 만드는 습관을 들이면 때때로 큰 실수로 들어가는 비용을 절감할 수 있다.

테스트 주도 개발 (TDD)

아래와 항목에 따라 개발하는 개발방법이다.

  • 테스트 할 실제 코드보다 테스트 코드를 먼저 만들며 개발한다.
  • 실패한 테스트를 성공시키기 위한 목적이 아닌 코드를 만들지 않는다.

TDD는 테스트를 먼저 만들고 실제 코드를 작성하기 때문에 테스트를 빼먹을 일이 적다.

TDD 기능 설계 테스트 전략과 예제

  • 추가하고 싶은 기능을 구현 전에 먼저 테스트 코드로 표현해본다.
  • 조건, 행위, 결과 3개로 표현하는 것이 좋다.
    • 일반적으로 when, if, then이라고 표현한다.
    • 나의 경우에는 if, action, then이 더 직관적으로 와닿는다. 아니면 조건 행위 결과라는 한국어를 그대로 쓰는 것도 괜찮은 것 같다.

유저를 가져오는 것에 실패하는 테스트를 작성한다면?

  • 조건: 유저가 존재하지 않는다.
  • 행위: 존재하지 않는 유저를 조회한다.
  • 결과: 유저가 존재하지 않는다는 예외를 던져준다.

위와 같은 방식으로 기능을 구현하기 전에 테스트 코드로 먼저 표현하며 개발을 진행해볼 수 있다.

JUnit 프레임워크의 동작 방식

  1. public void 메서드 시그니처를 가지고 있는 테스트 메서드를 모두 조회한다.
  2. 테스트 클래스에서 사용될 오브젝트를 하나 만든다. (이 메서드는 메 테스트마다 한번씩 생성되고 다시 버려진다.)
  3. @BeforeEach가 붙은 메서드가 있는지 찾고 각 테스트 전에 먼저 실행하게 된다.
  4. @Test가 붙은 메서드를 호출하고 테스트 결과를 저장해둔다.
  5. @AfterEach가 붙은 메서드가 있는지 찾고 각 테스트 이후에 실행하게 된다.
  6. 모든 테스트 메서드에 대해 2~5번을 반복한다.
  7. 모든 테스트의 결과를 종합해서 반환한다.

주의깊게 볼 점은 JUnit이 완전한 독립적 실행을 보장하기 위해 매번 테스트를 위한 오브젝트를 새로 생성했다가 버린다는 것이다. 덕분에 인스턴스 변수와 같은 것들도 마음껏 만들어도 다음 오브젝트 때 초기화될 것을 미리 알 수 있다.

픽스쳐

픽스쳐란 테스트에서 사용될 정보나 오브젝트를 말한다. 보통 @BeforeEach와 같은 메서드를 이용하여 매 테스트마다 적용할 수 있게 편리하게 사용한다.

스프링 테스트 구성하기

개선 전

public class UserDaoTest {
    UserDao userDao;

    @BeforeEach
    public void setUp() {
        ApplicationContext applicationContext = new GenericXmlApplicationContext("spring/applicationContext.xml");
        this.userDao = applicationContext.getBean(UserDao.class);
        ...
    }
    ...
}
  • 위 예제 코드의 경우 매번 스프링 컨테이너 오브젝트를 새로 만든다는 단점이 있다.
    • 스프링 컨테이너가 작을 때는 충분히 테스트할만 하겠지만, 스프링 컨테이너가 커지면 테스트 시간이 매우 많이 소요될 것이다.
      • ex) 시간을 많이 잡아먹는 초기화 빈, 리소스를 많이 할당받는 빈, 스레드를 띄우는 빈
    • 시간이 오래걸리는 것보다 더 큰 문제는 리소스를 깔끔하게 정리해주지 않았을 때, 일관적인 결과를 내지 않을 수도 있다는 것이다.

일반적으로 스프링을 테스트할 때 애플리케이션 컨텍스트를 공유한다.

개선 후

@ExtendWith(SpringExtension.class) // (JUnit5)
@ContextConfiguration(locations="/spring/applicationContext.xml")
public class UserDaoTest {
    @Autowired ApplicationContext applicationContext;
    UserDao userDao;

    @BeforeEach
    public void setUp() {
        System.out.println("applicationContext = " + applicationContext);
        System.out.println("this = " + this);
        this.userDao = this.applicationContext.getBean("userDao", UserDao.class);
        ...
    }
    ...
}
  • JUnit5에서 스프링 테스트하기 쉬운 환경을 제공한다.
  • spring-test 의존성을 받아오면 위와 같이 코드를 구성할 수 있다.
  • xml 설정정보를 가져와서 애플리케이션 컨텍스트를 @Autowired로 주입받을 수 있다.

이제 매번 동일한 ApplicationContext를 이용하여 테스트를 진행할 수 있다.
심지어 다른 클래스에서도 위의 애노테이션을 붙이면 하나의 애플리케이션 컨텍스트를 공유할 수 있다.

@Autowired의 활용

  • 일명 자동 와이어링이라는 기능이다.
  • 스프링 컨테이너에 올라간 빈을 집어올 수 있다.

DI는 인터페이스를 이용하는 것이 좋다

  • 추후 변경에 용이하다.
    • 그럼에도 별다른 큰 비용 없이 그냥 인터페이스 타입으로 받아주기만 하면 된다.
  • 테스트에도 도움된다.
    • 많은 구현체에 대한 테스트를 일일이 작성할 필요 없다.
    • 구현체의 복잡한 테스트를 할 필요 없이 인터페이스에 공개된 메서드를 작게 테스트하면 된다.

테스트를 위해 ApplicationContext 더럽히기

@BeforeEach
public void setUp() {
   DataSource dataSource = new SingleConnectionDataSource(
           "jdbc:postgresql://localhost/toby_spring",
           "postgres",
           "password",
           true
   );
   userDao.setDataSource(dataSource);
}
  • ApplicationContext에서 가져온 스프링 빈인 userDao를 테스트하기 위해 setter로 다른 DataSource를 넣어 오염시킬 수 있다.
  • 단, @DirtiesContext라는 애노테이션을 반드시 명시해주어야 한다.

테스트용 ApplicationContext 관리하기

  • 설정 .java 파일이나 설정 .xml 파일을 따로 관리하여 테스트용 Application을 관리할 수 있다.
  • applicationContext.xml 파일이 실제 사용하는 설정 정보라면, test-applicationContext.xml와 같은 방식으로 따로 관리하면 된다.

컨테이너 없이 테스트하기

  • 스프링 테스트임에도 스프링 컨테이너가 필수가 아닌 경우가 많다. 스프링 컨테이너를 띄우지 않아도 되는 테스트라면 띄우지 않는 것이 가장 좋다.

침투적 기술과 비침투적 기술

  • 침투적 기술: 애플리케이션 코드가 특정 기술 관련 API, 인터페이스, 클래스에 종속되는 것
  • 비침투적 기술: 기술에 종속되지 않는 순수한 코드를 유지할 수 있게 해주는 것

테스트 시 DI 방법 정리

  • 테스트 코드에 의한 DI (setter를 이용해 스프링 빈에 직접, @DirtiesContext)
  • 테스트를 위한 별도의 DI 설정 (test-ApplicationContext.xml와 같은 파일을 따로 관리)
  • 컨테이너 없는 DI 테스트

컨테이너가 없어도 된다면 항상 컨테이너가 없는 것이 최선이다.

학습 테스트

  • 애플리케이션의 로직보다는 API의 기능 확인 등을 위해 작성해보는 테스트

학습 테스트의 장점들

  • 자동화된 코드로 API를 테스트해보며 조건에 따라 기능이 어떻게 작동하는지 빠르게 확인할 수 있다.
  • 개발 중에 다시 참고하며 개발할 수 있다.
    • 다른 개발자와 공유도 가능하다.
  • 프레임워크, 제품 업그레이드 후에도 API가 같은 방식으로 작동하는지 알 수 있다.
  • 테스트 코드 작성에 좋은 훈련이 된다.
  • 레퍼런스만 보는 것보다 훨씬 재밌다.

스프링 프레임워크 프로젝트 내부에도 스프링 프레임워크를 개발하기까지 작성한 테스트들이 매우 많다. 그 테스트들을 레퍼런스로 참고하는 것도 매우 좋다.

버그 테스트

  • 코드에 오류가 있을 때, 해당 오류를 가장 잘 나타내주는 테스트를 만드는 것이다. 코드를 수정하여 버그 테스트가 성공하면 버그 수정이 성공한 것이다.

버그 테스트 장점

  • 테스트의 완성도를 높인다.
    • 기존의 테스트에 테스트가 하나 더 추가되는 것이다.
  • 테스트에서 버그의 내용을 명확히 나타내준다.
    • 이 버그 내용을 이용하여 다른 버그도 추가적으로 테스트할 수 있다.
  • 기술적인 문제를 해결하는데 도움이 된다.
    • 가장 단순한 코드와 그에 대한 버그 테스트를 만들어 기술적 문제 해결에 도움이 될 수 있다.

테스트에서 동등 분할과 경계값 분석

동등 분할

  • 작업의 결과가 한정적일 때, 값의 범위를 구분하여 구분되는 범위들을 커버하는 테스트를 작성하는 기법을 말한다.

경계값 분석

  • 최대, 최소, 문제가 되는 값의 주변 값 등을 테스트해보는 기법을 말한다.

정리

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

'프레임워크 > 토비의 스프링' 카테고리의 다른 글

토비의 스프링 5장 요약 정리 - 서비스 추상화  (0) 2022.09.06
토비의 스프링 4장 요약 정리 - 예외 처리  (0) 2022.06.21
토비의 스프링 3장 요약 정리 - 템플릿  (0) 2022.06.20
토비의 스프링 1장 요약 정리 - 오브젝트와 의존관계  (3) 2021.12.26
토비의 스프링 0장 정리  (0) 2021.12.14
    '프레임워크/토비의 스프링' 카테고리의 다른 글
    • 토비의 스프링 4장 요약 정리 - 예외 처리
    • 토비의 스프링 3장 요약 정리 - 템플릿
    • 토비의 스프링 1장 요약 정리 - 오브젝트와 의존관계
    • 토비의 스프링 0장 정리
    Jake Seo
    Jake Seo
    ✔ 잘 보셨다면 광고 한번 클릭해주시면 큰 힘이 됩니다. ✔ 댓글로 틀린 부분을 지적해주시면 기분 나빠하지 않고 수정합니다. ✔ 많은 퇴고를 거친 글이 좋은 글이 된다고 생각합니다. ✔ 간결하고 명료하게 사람들을 이해 시키는 것을 목표로 합니다.

    티스토리툴바