[Clean code] - 단위 테스트

Coodori·2023년 6월 30일
0

Study

목록 보기
1/10

작성하게 된 계기

최근에 테스트 코드 작성 문화를 손에 익을려고 노력하고 있었다.
하나의 테스트 코드에는 하나의 검사만 해야한다 등 통일하면 좋은 요소들이 있는 것 같아 이번 기회에 더 나은 테스트 코드를 작성하고자 공부를 하였다.

TEST

  1. 테스트는 유연성, 유지 보수성, 재사용성을 제공한다.
  • 테스트 코드의 중요성을 모르고 테스트만 하면 오케입니다~ 라는 마인드로 짜게되면 이후 기능이 추가되면서 많은 테스트 코드를 수정해야할 수 도 있다.
  1. 깨끗한 테스트 코드는 가독성이 우선이다.
  • 읽는 사람을 고려해야한다.
  1. 글처럼 읽어 나갈 수 있는 테스트 코드면 좋다.
    @Test
    public void turnOnLoTempAlarmAtThreashold() throws Exception {
     hw.setTemp(WAY_TOO_COLD); 
     controller.tic(); 
     assertTrue(hw.heaterState());   
     assertTrue(hw.blowerState()); 
     assertFalse(hw.coolerState()); 
     assertFalse(hw.hiTempAlarm());       
     assertTrue(hw.loTempAlarm());
    }
    나도 평소에 이러한 테스트 코드를 작성한다. 하지만 이러한 테스트코드는 이러한 형태로 변화할 수 있다.
    @Test
    public void turnOnLoTempAlarmAtThreshold() throws Exception {
     wayTooCold();
     assertEquals("HBchL", hw.getState()); 
    }
    ...
    public String getState() {
     String state = "";
     state += heater ? "H" : "h"; 
     state += blower ? "B" : "b"; 
     state += cooler ? "C" : "c"; 
     state += hiTempAlarm ? "H" : "h"; 
     state += loTempAlarm ? "L" : "l"; 
     return state;
    }
  • 대문자일 경우 작동 o
  • 소문자일경우 작동 x 장점: WHEN(주어진 환경)을 이해 할 수 있고 THEN에 대한 결과값을 한줄에 가독성 있게 읽을 수 있다.

    추가로 테스트 코드 환경은 자원이 제한적인 경우가 거의 드물어서 코드의 깨끗함(가독성)을 높이는 편을 추구하는 경향이 있다고 한다.
    운영 환경일 경우 StringBuffer등 자바 운영환경에서 효율적인 구조를 사용해야할듯하다.

  1. 테스트당 증명해야하는 테스트는 하나
  • 여러개를 사용했을 때 무엇을 테스트하고 싶은지 파악하기 힘들다.
/**
 * addMonth() 메서드를 테스트하는 장황한 코드
 */
public void testAddMonths() {
  SerialDate d1 = SerialDate.createInstance(31, 5, 2004);

  SerialDate d2 = SerialDate.addMonths(1, d1); 
  assertEquals(30, d2.getDayOfMonth()); 
  assertEquals(6, d2.getMonth()); 
  assertEquals(2004, d2.getYYYY());
  
  SerialDate d3 = SerialDate.addMonths(2, d1); 
  assertEquals(31, d3.getDayOfMonth()); 
  assertEquals(7, d3.getMonth()); 
  assertEquals(2004, d3.getYYYY());
  
  SerialDate d4 = SerialDate.addMonths(1, SerialDate.addMonths(1, d1)); 
  assertEquals(30, d4.getDayOfMonth());
  assertEquals(7, d4.getMonth());
  assertEquals(2004, d4.getYYYY());
}

라는 테스트 코드를

  SerialDate d1 = SerialDate.createInstance(31, 5, 2004);
  
  SerialDate d3 = SerialDate.addMonths(2, d1); 
  assertEquals(31, d3.getDayOfMonth()); 
  assertEquals(7, d3.getMonth()); 
  assertEquals(2004, d3.getYYYY());
}

느낌으로 30일때 31일때 나눌 것 같긴하다.
5. F.I.R.S.T

  • 빠르게(Fast)
  • 독립적으로(Independent)
    - 테스트끼리 의존하면 안된다.(실패 장소 분석 X)
  • 반복가능하게(Repeatable)
    - 어느 환경에서도 가능해야한다.
    • 노트북, 데스크탑
  • 자가검증하는(Self-Validating)
    - true or flase 로 정확하게 결과를 내야한다.
  • 적시에(Timely)
    - 실제코드를 구현하기 직전에 구현
    • 실제 코드를 구현한 다음에 테스트 코드를 만들면 테스트가 어려운 사실 발견가능
    • 해당 부분에선 생산성과 테스트간의 TDD관련 논쟁이 있었다.

번외 TDD

Test-Driven-Development으로 '테스트 주도 개발'

코드가 내손을 벗어나기전 가장 빠르게 피드백을 받을 수 있다.


RED : 테스트 실패
GREEN : 테스트 성공
REFACTOR : 리팩토링

스프링에서 TDD => 의존성이 작은 것부터 점점 생겨가는 순서로 진행을 한다.

  • Repository -> Service -> Controller 순서로 개발을 진행한다.
  • Repository 계층의 테스트는 H2와 같은 인메모리 데이터베이스 기반의 통합 테스트로 진행한다.
  • Service 계층의 테스트는 Mockito를 사용해 Repository 계층을 Mock하여 진행한다.
  • Controller 계층의 테스트는 SpringTest의 MockMvc를 사용해 진행한다.

사실상 Flow를 따라가는 것과 비슷하다.

  1. 단점: 바로 위에서 말한 생산성 저하

결론

실제 코드의 유연성, 유지보수성, 재사용성을 강화하고 지속적으로 깨끗하게 관리하며
자신이 짠 코드에 대한 불안정성을 개선하자.

Reference

https://github.com/Yooii-Studios/Clean-Code/blob/master/Chapter%2009%20-%20%EB%8B%A8%EC%9C%84%20%ED%85%8C%EC%8A%A4%ED%8A%B8.md
http://clipsoft.co.kr/wp/blog/tddtest-driven-development-%EB%B0%A9%EB%B2%95%EB%A1%A0/
https://mangkyu.tistory.com/182

profile
https://coodori.notion.site/0b6587977c104158be520995523b7640

0개의 댓글