[실용주의 단위 테스트] 6장_무엇을 테스트할 것인가?

무엇을 테스트해야하는지 정리한다.
이것으로 쉽게 요약할 수 있다 : Right-BICEP

  1. Right : 결과가 올바른가 ?
  2. B : Boundary. 경계 조건은 맞는가 ?
  3. I : Inverse relationship. 역관계를 검사할 수 있나 ?
  4. C : Cross check. 다른 수단을 활용해서 교차 검사 가능한가 ?
  5. E : Error. 오류를 강제로 발생시킬 수 있는가 ?
  6. P : Performance. 성능 조건은 기준에 부합하나 ?

1. Right

테스트 코드는 무엇보다도 먼저, 기대한 결과를 산출하는지 검증 해야한다.
다음 코드의 ScoreCollection 에 더 많은 숫자나 더 큰 수를 넣어서 테스트를 강화할 수 있다. 하지만 이러한 테스트는 행복 경로 테스트의 영역일 뿐이다.

1
2
3
4
5
6
7
8
9
10
11
12
public class ScoreCollectionTest {
@Test
public void answersArithmeticMeanOfTwoNumbers() {
ScoreCollection collection = new ScoreCollection();
collection.add(() -> 5);
collection.add(() -> 7);

int actualResult = collection.arithmeticMean();

assertThat(actualResult, equalTo(6));
}
}

2. Boundary

대부분의 결함은 Corner Case 이다. 테스트로 이것들을 처리해야한다. 다음과 같은 경계 조건이 있다.

  1. 모호하고 일관성 없는 입력 값. ex) 특수문자가 포함된 파일 이름
  2. 잘못된 양식의 데이터
  3. Overflow 를 일으키는 계산
  4. 비거나 빠진 값. ex) 0, “”, null
  5. 이성적인 기대 값을 벗어나는 값. ex) 200 세의 나이
  6. 중복을 허용해서는 안되는 목록에 중복 값이 있는 경우
  7. 정렬이 안된 정렬 리스트 혹은 그 반대
  8. 시간 순이 맞지 않는 경우. ex) HTTP Server 가 OPTIONS 메서드의 결과를 POST 메서드 보다 나중에 반환

다음 클래스를 기준으로 테스트해보자.

1
2
3
4
5
6
7
8
9
10
11
12
public class ScoreCollection {
private List<Scoreable> scores = new ArrayList<>();

public void add(Scoreable scoreable) {
scores.add(scoreable);
}

public int arithmeticMean() {
int total = scores.stream().mapToInt(Scoreable::getScore).sum();
return total / scores.size();
}
}
Read more

Logback

Logback 이 무엇이고, 어떻게 사용하는지 정리한다.

1. SLF4J (Simple Logging Facade for Java)

우선 SLF4J 가 무엇인지 알아보자.
공식 문서 (http://www.slf4j.org/manual.html) 에 따르면, SLF4J 는 java.util.logging, logback 및 log4j 와 같은 다양한 Logging Framework 에 대한 Facade 또는 추상화 역할을 한다.
그래서, SLF4J 는 개발자가 원하는 Logging Framework 를 plug-in 할 수 있도록 지원한다. 예를 들면, SLF4J Facade 덕분에 log4j 구현체를 사용하다가 logback 구현체로 변경하여도 일관된 방식으로 logging 이 가능하다.

2. Logback

logback 은 log4j 의 후속 프로젝트이다. 왜 log4j 대신에 logback 를 사용해야하는지는 공식 문서 (http://logback.qos.ch/reasonsToSwitch.html) 를 참고하자.
이제 실제 프로젝트에 적용을 해보자.

3. 의존성 확인

의존 관계 tree 를 명확히 확인하기 위해 maven 기반으로 프로젝트를 생성하였다.
pom.xml 에는 다음과 같이 spring-boot-starter 가 포함되어 있다.

그리고, spring-boot-starter 를 따라가면, 다음과 같이 spring-boot-starter-logging 이 포함되어 있다.

Read more

[전문가를 위한 스프링 5] 3장_Spring IoC 와 DI

1. IoC 종류

IoC (Inversion of Control ) 은 두 가지로 나뉜다 : DI, DL
DI 는 IoC 컨테이너가 컴포넌트에 의존성을 주입시켜 준다. 반면, DL 은 컴포넌트 스스로 의존성 참조를 가져온다.

  • DI (Dependency Injection)

    • 생성자 주입 : IoC 컨테이너는 해당 컴포넌트를 초기화할 때, 컴포넌트에 필요한 의존성을 전달

      1
      2
      3
      public ConstructorInjection(Dependency dp){
      this.dp = dp;
      }
    • 세터 주입 : setter method 를 호출해서, 의존성을 나중에 제공 가능

      1
      2
      3
      public void setDependecy(Dependency dp){
      this.dp = dp;
      }
    • 필드 주입 : 스프링 컨테이너가 reflection 을 이용해 필요한 의존성을 주입

      1
      2
      3
      4
      5
      @Service
      public class Singer {
      @Autowired
      private Inspiration inspirationBean;
      ...
  • DL (Dependency Lookup)

    • Depenency Pull : 중앙 registry 에서 의존성을 직접 가져오는 방식 ( register -> container )

      1
      context.getbean();
    • Contextualized Dependency Lookup : 특정 중앙 registry 에서 의존성을 가져오는 것이 아니라, 자원을 관리하는 컨테이너에서 의존성을 가져오는 방식

      1
      2
      3
      publicv void lookup(Container ct){
      this.dp = (Dependency) ct.getDependency("myDependency");
      }

그렇다면, 의존성 주입 vs 의존성 룩업 ? 의존성 주입을 사용해라. 주입을 이용하면,

  • 사용자 클래스는 IoC 컨테이너와 완전리 분리된다.
  • 직접 테스트용 의존성을 주입하기 쉬우므로 테스트 하기 쉽니다.

그렇다면, 생성자 주입 vs 세터 주입 vs 필드 주입? 상황에 따라 선택해라.

  • 생성자 주입 : 컴포넌트에 의존성 주입을 보장해야할 때
  • 세터 주입 : 새로운 객체를 생성하지 않고 의존성을 교체할 때
  • 필드 주입 : 다음 이유로, 권장하지 않는다.
    • 클래스가 비대해지는 상황을 생성자 주입이나 수정자 주입에서는 쉽게 알아 챌 수 있지만, 필드 주입은 아니다.
    • 클래스는 public interface 의 메서드나 생성자로 필요한 의존성 타입을 명확히 전달해야하는데, 필드 주입을 이용하면 어떤 타입의 의존성이 필요한지 명확하지 않다.
    • final 필드에 사용할 수 없다.
    • 의존성을 수동으로 주입해야하므로, 테스트 코드 작성이 어렵다.

2. BeanFactory, ApplicationContext

  1. Bean : 컨테이너가 관리하는 모든 컴포넌트
  2. BeanFactory interface : 컴포넌트의 라이프사이클과 의존성을 관리
  3. ApplicationContext interface (extends BeanFactory) : DI 외에도, 트랜잭션, AOP, 애플리케이션 이벤트 처리 기능 제공

3. Application Context 구성

Read more

[실용주의 단위 테스트] 5장_좋은 테스트의 FIRST 속성

의미있는 테스트를 만드는데 도움을 주는 핵심 개념을 정리한다.
좋은 테스트 조건 : FIRST ( Fast / Isolated / Repeatable / Self-validating / Timely )

1. Fast

테스트를 빠르게 유지해라. 단위 테스트를 하루에 서너 번 실행하기도 버겁다면 잘못된 것이다. 예를 들면, 모든 테스트 코드가 데이터베이스를 호출하면 전체 테스트 역시 느릴 것이다.

2. Isolated

좋은 단위 테스트는,

  1. 검증하려는 작은 양의 코드에 집중한다. ‘단위’ 라고 말하는 정의와 부합한다.
  2. 다른 단위 테스트에 의존하지 않는다. 만약, 여러 테스트가 값 비싸게 생성된 데이터를 재사용 하는 방식으로 테스트 순서를 조작해서 전체 테스트의 실행 속도를 높이려 할 수도 있다. 하지만, 이것은 의존성의 악순환만 발생시킨다. 테스트 실패시, 무엇이 원인인지 알아내느라 오래 걸릴 수 있다.

객체 지향 설계에서 SRP 는 클래스를 변경해야할 이유가 하나민 있어야한다고 말한다. 테스트 메서드도 마찬가지다. 테스트 메서드가 하나 이상의 이유로 깨지면 테스트를 분할해라.
그리고, 테스트에 두 번째 단언을 추가할 때 다음을 먼저 고민해보자.

이 단언이 단일 동작을 검증하도록 돕나 ?

아니면, 새로운 테스트 이름으로 기술할 수 잇는 어떤 동작인가 ?

3. Repeatble

좋은 테스트는, 실행할 때마다 결과가 같아야한다. 이러기 위해서는, 직접 통제할 수 없는 외부 환경과 격리시켜야한다.

Read more

[실용주의 단위 테스트] 4장_테스트 조직

이번 장은 다음 주제를 다룬다.

  1. 준비-실행-단언을 사용해 테스트를 가시적이고 일관성 있게 만드는 방법
  2. 메서드를 테스트하는 것이 아니라, 동작을 테스트
  3. 테스트 이름의 중요성
  4. @Before, @After

1. AAA 로 테스트 일관성 유지

AAA 는 다음과 같다.

  • Arrange : 준비. 테스트 코드를 실행하기 전에 시스템에 적절한 상태에 있는지 확인
  • Act: 실행. 테스트 코드 실행
  • Assert : 단언. 실행한 코드가 기대한 대로 동작하는지 확인

예를 들면,

1
2
3
4
5
6
7
8
9
10
@Test
public void answerArithmeticMeanOfTwoNumbers() {
ScoreCollection collection = new ScoreCollection();
collection.add(() -> 5);
collection.add(() -> 7);

int actualResult = collection.arithmeticMean();

assertThat(actualResult, equalTo(6));
}

2. 동작 테스트 vs 메서드 테스트

단위 테스트를 작성할 때는, 개별 메서드를 테스트 하는 것이 아니라 클래스의 종합적인 동작을 테스트해야한다.

3. 테스트와 프로덕션 코드의 관계

Read more