본문 바로가기
Clean Code

[클린 코드] 9장 - 단위 테스트 (Unit Tests)

by 노력남자 2022. 3. 11.
반응형

이번 포스팅에선 클린 코드 9장 - 단위 테스트 (Unit Tests)에 대해 알아보겠습니다.

 

단위 테스트란 (Unit Test)?

 

유닛 테스트(unit test)는 컴퓨터 프로그래밍에서 소스 코드의 특정 모듈이 의도된 대로 정확히 작동하는지 검증하는 절차다. 즉, 모든 함수와 메소드에 대한 테스트 케이스(Test case)를 작성하는 절차를 말한다. - Wikipedia

 

개별 컴포넌트가 정상적으로 작동하는지 테스트 케이스를 만들어 테스트 하는 걸 말한다.

 

주로 JUnit, Mockito를 사용해 테스트를 한다.

 

이외에도 Integration Test, E2E Test가 있는데 따로 언급하진 않겠다.

 

왜 테스트를 짜야 할까?

 

테스트는 유연성, 유지보수성, 재사용성을 제공한다.

 

일단 잘 돌아가는지 테스트를 하는 목적도 있지만 변경을 쉽게 하기 위함이 더 크다.

 

만약 테스트 코드가 없는 코드를 수정한다고 하면?

 

개발자는 내가 고친 코드가 어떤 영향을 끼칠지 몰라 겁이나 항상 노심초사 해야한다.

 

테스트 코드가 잘 짜여진 코드를 수정한다고 하면?

 

코드를 잘못 고쳐도 테스트에서 잡아주니 맘편히 수정할 수 있다.

 

어떻게 테스트를 잘 짤 수 있을까?

 

1. 깨끗한 테스트 코드

 

테스트 코드도 코드다.

 

코드에서 제일 중요한 건 세가지가 있는데 바로 가독성, 가독성, 가독성이다.

 

테스트 코드에선 가독성이 더욱 더 중요하다.

 

그냥 만들어 놓고 잘 돌아가겠지 하고 냅두는 게 아니라 추후에 수정이 필요하기 때문에 잘 읽히게 작성해야 한다.

 

명료성, 단순성, 풍부한 표현력으로 테스트 코드를 작성하자.

 

 

2. F.I.R.S.T.

 

기본적인 F.I.R.S.T. 원칙이다.

 

잘 따르자.

 

Fast : 테스트는 빨라야 한다. 테스트는 자주 돌려야 하기 때문에 느리면 안 된다.

 

Independent : 각 테스트는 서로 의존하면 안 된다. 의존 되어있으면 테스트가 연달아 실패하기 때문에 원인을 진단하기 어렵다.

 

Repeatable : 어떤 환경에서든 반복가능해야 한다. local, QA, Staging, 운영 환경 어디서든 테스트가 돌아가야 한다. 반드시!

 

Self-validating : 테스트는 bool 값으로 결과를 내야 한다. 성공 or 실패

 

Timely : 테스트는 적시에 작성해야 한다. 단위 테스트는 테스트하려는 실제 코드를 구현하기 직전에 구현한다.

 

 

3. 테스트 당 assert 하나

 

테스트 당 1개의 assert문을 사용하자.

 

하나의 테스트에 1개 이상 assert문을 사용하면 이 테스트에서 어떤 걸 검증하려고 하는지 한눈에 파악이 불가능하다.

 

@Test
public void testAddStudent() {
    // given
    Student student = new Student("effortGuy", 1);

    // when
    studentRepository.save(student);

    // then
    assertThat(studentRepository.findAll().size(), 1);
}

 

리턴 값의 전부 비교 해야 하는 그런 테스트는 여러 개의 assert문을 어쩔 수 없이 사용해야 한다.

 

최소한으로 assert문을 사용하자.

 

 

4. 테스트 당 개념 하나

 

@Test
public void testTeacher() {
    Student student1 = studentRepository.save(new Student("김노력", 1));    
    assertEquals("김선생", student1.teacher.name);

    Student student2 = studentRepository.save(new Student("정노력", 2));
    assertEquals("정선생", student2.teacher.name);

    Student student3 = studentRepository.save(new Student("강노력", 3));
    assertEquals("강선생", student3.teacher.name);
}

 

학생의 이름과, 반을 저장한 후 그 학생의 선생님 이름을 비교하는 테스트다.

 

1반인 경우, 2반인 경우, 3반인 경우 3가지 개념을 하나의 테스트로 묶어놨다.

 

단점

 

1. 만약 위 테스트에서 2반 테스트가 실패했다면? 어떻게 알 수 있을까? 하나씩 찍어봐야 한다. 어후..

 

2. 어떤 테스트인지 한눈에 들어오지 않는다. 분석해야 한다. ㅠㅠ

 

@Test
public void test1Ban() {
    Student student1 = studentRepository.save(new Student("김노력", 1));
    assertEquals("김선생", student1.teacher.name);
}

@Test
public void test2Ban() {
    Student student2 = studentRepository.save(new Student("정노력", 2));
    assertEquals("정선생", student2.teacher.name);
}

@Test
public void test3Ban() {
    Student student3 = studentRepository.save(new Student("강노력", 3));
    assertEquals("강선생", student3.teacher.name);
}

 

위처럼 개념당 하나의 테스트를 작성하니

 

1. 한눈에 잘 들어온다.

 

2. 어떤 테스트가 실패했는지 잘 돌았는지 볼 수 있다.

 

그래서! 테스트는 반드시 개념당 하나의 테스트를 작성하자.

 

 

5. Build - Operate- Check, Given - When - Then, DCI

 

테스트 코드를 작성할 땐 아래와 같이 작성하면 보기 좋다.

 

Build, Given

 

첫 부분은 테스트 자료를 만든다.

 

Operate, When

 

두 번째 부분은 테스트 자료를 조작한다.

 

Check, Then

 

세 번째 부분은 테스트를 검증한다.

 

 

@Test
public void testAddStudent() {
    // builde, given
    Student student = new Student("effortGuy", 1);

    // operate, when
    studentRepository.save(student);

    // chekc, then
    assertThat(studentRepository.findAll().size(), 1);
}​

 

+ DCI 패턴이라고 있는데 자주 사용하는 제가 자주 사용하는 테스트 방식입니다. 한번 구경해보세요.

 

 

사실 테스트 하는 방법은 정말 방대한데 일부만 그냥 간단히 정리된 장이다.

 

TDD, BDD 등 개발 방법론이 많고 테스트 종류, 테스트 라이브러리 등 정말 알아야 할 게 많다.

 

블로그에 JUnit, Mockito 총 정리한 자료가 있으니 한번 보는 것도 괜찮을 거 같다.

반응형

댓글