OKKY The Real TDD 후기

No Image

테스트하기 쉬운 코드로 개발하기 - 정진욱님

테스트하기 쉬운 코드란?

  • 같은 입력에 항상 같은 결과를 반환하는 코드 - Deterministic
    • 항상 결과가 같지 않는 코드란?
      • 시간에 의해 결과가 결정되는 함수.
        • 오늘 날짜를 구하는 함수
      • DB의 의해 결과가 결정되는 함수.
        • 전체 회원의 수를 구하는 함수
  • 외부 상태를 변경하지 않는 코드 - No side effect
    • 외부 상태를 변경하는 함수란?
      • Console.WriteLine(result);

테스트 시나리오는 어떻게 작성할까?

  • 유효성 검사
    • Email : @가 포함되었는가?
    • 이름 : 숫자가 포함되었는가?
    • ConferenceId : 음수인가?
  • 이미 등록된 좌석 수 DB에서 읽어오기
  • 요청한 좌석 수가 확보 가능한지 판단하기
    • 잔여 좌석수(전체 좌석 수 - 등록된 좌석 수) >= 요청한 좌석수
    • 한꺼번에 10좌석을 초과해서 등록할 수 없다.
  • 등록 정보 저장
  • HTTP 결과 반환

위 테스트 시나리오 중 테스트하기 어려운 부분 어느 곳일까?

  • 이미 등록된 좌석 수 DB에서 읽어오기
    • DB는 항상 같은 입력(Query)에 항상 같은 결과를 반환하지 않는다.
  • 등록 정보 저장

외부 상태에 의존적인 코드.

어떻게 이 문제를 해결할까?

  • 테스트하기 쉬운 코드와 어려운 코드를 분리하자.
    • 외부 상태에 의존적인 부분이 어디인지 확인하고 분리하자.
      • DB를 조회하는 부분
      • 테스트 데이터를 넣고 테스트.
  • 외부 상태에 의존 적이지 않는 코드
    • 즉 테스트를 하기 쉬운 코드는 도메인 객체에

테스트하기 쉬운 코드와 테스트하기 어려운 코드가 만나는 부분을 최대한 end-point(controller) 쪽으로 작성하여 결합도를 낮춘다.

TDD 맛보기

이메일에_골뱅이가_없으면_유효하지_않은_형식입니다(){
    //Arrange
    var sut = new ConferenceRegistration{Email = "nesoy.gmail.com"};

    //Act
    string actual = sut.validate();

    //Assert
    Assert.Notnull(actual);
}

TDD로 code를 작성할 때 주의할 점.

  • 테스트를 만족하는 만큼만 코드를 작성하자.
    • 그 이상 코드를 작성하게 되면 테스트로 커버할 수 없다.
    • 즉 코드의 변경되는 부분을 놓칠 수 있다.
    • 작게 작게 변경되는 부분에 집중하고 Cover하자.

두 부류 코드(테스트 하기 쉬운 코드, 테스트 하기 어려운 코드)가 만나는 Edge에서 어떻게 테스트하는가?

  • 수동 테스트
    • curl, Postman
  • 자동 테스트
    • DB에 테스트 데이터를 저장하고
    • POST로 API를 호출합니다.
    • 결과 값을 검증하는 과정을 진행합니다.

Mock 사용 한다는 건?

  • 작성된 코드사용을 강제할 수 있다.
  • Mock을 통해 이음새(Seam) 도입
  • 행위 검증(mock)
    • 어떤 메소드가 호출되었는가?
    • 추상화가 필요함. -> 불필요한 추상화가 필요
    • Outside-in
  • 상태 검증(value)
    • 결과 값이 무엇인가?
    • 불필요한 추상화 필요없음.
    • 구현된 코드에만 의존하기 때문에 실제로 데이터가 필요.
    • Inside-out

Mock의 단점은 무엇일까?

  • Mock이 사용하기 쉬우니 남발할 가능성이 크다.
    • 대부분 Mock 사용 예제는 간단하다. 그래서 장점이 크게 보인다.
    • 실제 프로젝트에 적용하면 한꺼번에 많은 수의 Mock을 다루면서 곤란을 겪는다.
  • 적당 수의 Mock 사용에 대한 답을 찾기 어렵다.
  • 때로는 상태 검증으로 돌아가보자.

Mock을 대신하여 상태 검증를 한다는 것은?

  • TDD를 통한 사전이 아니라 사후 테스트를 하자.
  • 난해한 코드가 아니다.
  • 구현된 코드를 사용하지 않고 굳이 어려운 길을 택할 이유가 없다.
  • 완벽을 추구하면서 Mock을 사용하는 비용을 들일 필요가 있는가?

정리하자면?

  • 테스트하기 쉬운 코드란?
    • 항상 같은 결과 반환
    • 외부 상태를 변경하지 않는 코드
  • 테스트하기 쉬운 코드로 개발하기
    • 테스트하기 쉬운 코드와 어려운 코드 분리
    • 두 부류의 코드는 최대한 가장 자리에 위치(예외: 로깅, 퍼사드)
    • 테스트하기 어려운 코드를 최대한 가장자리에서 만나게 만들자.
      • endpoint

Reference

후기

  • 과도한 Mock은 정답이 아닌듯 하다.
  • Mock과 상태 검증 적절히 사용하는 것이 중요한 Point라고 생각된다.

의식적인 연습으로 TDD, 리팩토링 연습하기 - 박재성님

  • TDD < Refactoring

의식적인 연습이란?

  • 무조건 연습을 많이 한다고 잘할 수 있을까?
    • 테스트하기 쉬운 코드와 테스트하기 어려운 코드를 보는 눈
    • 테스트하기 어려운 코드를 테스트하기 쉬운 코드로 설계하는 감(sense)
    • 목적 의식 있는 연습에 얼마나 많은 시간을 투자했느냐?
      • 1만 시간의 재발견
    • 구체적인 목표 세우고 실행하기
    • 피드백과 피드백에 따른 행동 변경을 수반

의식적인 연습으로 TDD, 리팩토링 연습 과정

  • 단위 테스트 연습
    • 사용하는 API 사용법을 익히기 위한 학습 테스트 작성 시작
    • xUnit의 사용법을 익힐 수 있다.
    • 사용하는 API에 대한 학습 효과가 있다.
    • input과 output이 명확한 클래스 메소드(보통 util 성격의 메소드)
  • TDD 연습
    • 토이 프로젝트로 시작하기
    • 의존관계가 없는 프로젝트로 시작하기
  • Refactoring 연습
    • Method 분리하기
    • Depth가 2단계 이상
    • Else를 안 쓰는 연습
    • Method는 항상 SRP를 지켜야 한다.

한 번에 한 가지 명확하고 구체적인 목표를 가지고 연습하라. 연습은 극단적인 Rule을 세워 연습하는 것도 좋다. 그래야 insight도 생긴다.

극단적인 Rule이라면?

  • 일급 Collection만 사용하기.
  • 3개 이상의 인스턴스 변수를 가진 클래스를 쓰지 않는다.
  • 점진적으로 요구사항이 복잡한 프로그램을 구현한다.

의존관계 추가를 통한 난이도 높이기

  • 앞 단계 연습을 잘 소화했다면 테스트하기 쉬운 코드와 어려운 코드를 분리하여 연습하기
  • ATDD 기반으로 응용 애플리케이션 개발하기
  • 컴파일 에러를 최소화하면서 Refactoring

Book

Question

  • comfort zone을 나오는 노하우는 있으신지?
    • 시간과 마음적인 여유 확보하기.
    • 확보한 시간과 여유로 의식적인 연습하기.
  • 요구 사항이 변경되는 경우 테스트 변경 또한 많은 경우에는 어떻게 해야할까?
    • 제대로 설계가 안된 경우.
      • 설계에 다시 고민하고 생각하기.
    • 변경이 한 곳으로 집중되어야 한다.

후기

  • 가장 재밋게 듣고 이해와 공감이 많이 가는 강의였다.
  • 시간이 괜찮다면 Toy Project로 시작해봐야겠다.
    • 우선 시간과 여유 확보하기.

코드 품질을 위한 테스트 주도 개발 - 한성곤님

TDD란?

  • Fail Test
  • Write Production Code
  • Refactoring

From Test

  • 동작하는 코드에 대한 자신감
  • 회귀테스트를 통한 자유로운 리팩토링
  • 코드에 대한 지식이 증가
  • 개발 생산성 향상

From Test-first

  • 과도한 설계를 피하고, 간결한 interface를 가짐
  • 불필요한 기능(Gold-Plating)을 줄임
  • 실행 가능한 문서(Executable documents)를 가짐
  • 코드 품질을 높임

TDD Metric

  • Software Quality
    • External Quality + Internal Qulity
  • Internal Qulity
    • Coding Rules : 개발 표준 및 기본적인 관례 준수
    • Potential Bugs : Inspection을 활용한 점검
    • Comments : 적정한 주석, Public Method만 확인
    • Duplication : DRY
    • Complexity : 적절한 분포
    • Unit Tests : Lack of Unit Test

후기

  • 생각보다 측정하는 방식이 다양하고 측정하는 Metric 또한 너무 많았다.
  • 단순히 Coverage가 높다고 좋은 Software는 아니라고 말씀해주신게 기억이 남는다.

테알못 신입은 어떻게 테스트를 시작했을까? - 이혜승님

  • 이미 테스트 하기 쉽게 만들어져 있는 코드로 시작한다.
    • 순수 함수
    • 외부 의존성(Dependency)이 없는 함수.
    • utility, helper
  • 테스트 하기 어렵게 만들어져 있는 코드에서 테스트 하기 쉬운 것만 분리하기
    • 중요도가 높은 비즈니스 로직이 포함된 부분
    • 버그가 발견된 부분(과거 X)
    • 결합이 낮고 논리는 복잡한 부분
  • 외부 의존성을 제거하기 위해 Parameter로 작성하기.
  • 중요도가 높은 비즈니스 로직부터 시작하기.

TDD의 좋은 점은?

  • 불안감이 제거된다.
  • 스펙 문서 가능
    • 실제로 파라미터 뭐가 있는 지, 반환 값은 어떤 모양인지 등 스펙을 확인할 때 테스트 코드를 봅니다.
  • 디자인 개선 효과
    • 테스트 코드를 통해서 디자인 상의 결점을 꽤 자주 발견
  • 학습 동기부여
  • 개발 생산성 향상
    • 버그를 추적하는 시간이 현저하게 줄어들어요.
    • 테스트 안 해서 아낀 시간 <= 테스트 안 해서 나온 버그 고치는 시간
  • 프로젝트 생산성 향상
    • 비즈니스 로직의 허점을 사전에 발견하기도 해요
  • 집중력 향상
    • 동시에 한 가지 이상의 일을 하지 않도록 통제해준다.

TDD를 진행하면서 실수는?

  • 테스트 대상 오류
    • 비즈니스와 관련된 버그를 낼 가능성이 낮거나 없고,
    • 테스트를 유지함으로써 얻는 이익 < 테스트 유지와 관리에 드는 비용일 때
    • 테스트가 단언하고 있는 내용이 사용자에게 중요한 가치를 주는 것이 아닐때는 작성하지 않는다.
  • 검증력이 떨어지는 테스트
    • 잘못된 검증.
    • 차라리 안하는게 낫다.
  • 테스트를 앞서가는 프로덕션 코드
    • 앞서나가는 코드를 작성하게 되면 빈틈이 생기게 된다.
    • 빈틈을 통해 버그가 발생할 수 있는 가능성.
  • 함수가 곳곳에 퍼지게 된다.
    • 낮은 결합도는 얻었지만 높은 응집력은 얻지 못했다.

무엇을 검증할 것인가?

  • 비즈니스 가치를 주는 부분을 테스트 하자.
  • 테스트 코드도 코드. 개선하고 유지보수한다.

후기

  • 비슷한 경험도 했고 테스트를 작성하기에 앞서 비즈니스 가치를 주는 지 확인하는 작업도 필요해 보인다.

테스트를 돌보기 위한 간단한 실천 방법, 효과 - 양완수님

  • 귀찮은 것.
  • 고통스럽게 하는 것.
    • 숨겨진 본질
    • 테스트 욕심쟁이
      • 테스트가 실패하는 이유는 단 하나
      • 하나의 테스트는 오직 한 가지만 똑바로 검사해야 한다.
    • 무엇을 테스트하는지? 인지능력의 과부화
      • 흩어진 코드와 데이터
      • 매직넘버
    • 테스트가 깨지기 쉬운 것들
      • 높은 결합
      • 낮은 응집
  • 추상이란?
    • 문맥(Context) 위에서 오직 관심 있는 것들에 대해서만 집중하여 명확하게 하는 것.
  • 숨겨진 본질
    • 낮은 추상화
    • 들쭉날쭉한 추상화
    • 끊어진 논리
    • 알 수 없는 의도

Reference

후기

  • 테스트 코드도 프로덕션 코드처럼 개선해나가는 모습이 인상적이였다.

당신의 TDD가 항상 실패하는 이유 - 이규원님

우리가 제어할 수 없는 것

  • 외부 세상
    • 현실 세계
    • 인프라
    • 외부 서비스
    • 레거시

설계

  • 낮은 결합
  • 높은 응집
  • 도메인 모델 보호
  • 정보 숨김(information hiding)
    • 어려운 설계 결정과 변경될 가능성이 높은 설계 결정들을 다른 모듈로부터 숨기는 것.
    • 인터페이스 테스트
  • 반복 주기
    • 계획
    • 실행
    • 평가
  • 공유하는 문화
    • 목표
    • 지식
  • 도메인 모델과 플랫폼
    • 도메인 모델은 플랫폼에 독립적이어야 한다.

단위 테스트는 통과되지만 항상 안전할까?

  • 항상 안전하지 않기에 통합 테스트도 필요하다.
    • 메뉴얼 테스트
    • 기능 테스트
  • 외부 연동 테스트인 경우?
    • Fake Service를 만들어서 진행

후기

  • 단위 테스트로는 발견할 수 없는 부분도 있는 실제 사례를 직접보게 되어 기쁘다.
    • 사내에서도 통합 테스트의 필요성에 대해 조언을 많이 해주셨다.

Q & A

왜 TDD가 필요한가?

  • 나를 위해서 하자.
  • 전파하는 건 나중에 하자.
    • 스스로 필요성을 느끼게 하는것이 가장 중요.
  • TDD 좋은 점은?
    • 삶의 만족도를 높여준다.
    • 빠른 피드백을 통해 직무의 탈진을 낮춰준다.
  • TDD는 어디서 생겼을까?
    • Pair Programming을 하면서 생겼다.
    • 먼저 애자일이란 환경이 갖춰줘야 한다.
      • 애자일은 사회적인 행위.
      • 나만 하는 것이 아니라 다같이 하는 행위
  • 시간이 많이 필요합니다.
  • 작은 변화를 일으키기 위해서는?
    • 많은 도전과 실패하기
  • TDD를 도입하려면?
    • 한번에 도입하려 하지 말고 작은 Step 진행하자.
    • 한번의 하나의 실패
    • 테스트의 순서가 중요.

첫 테스트를 작성할때 핵심을 테스트하자. 첫 단추부터 잘못 작성하게 되면 방향성 잃게 되어 테스트의 작성 방향이 완전 다르게 진행된다.

커버리지는 의미 있는 숫자인가?

  • Metric에 초점을 맞추게 되면 잃어버리는게 많다.
    • 테스트의 품질도 중요하다.
    • 지표를 높이기 위한 테스트는 좋지 않다.
  • 테스트 커버리지에 초점을 맞추지 말자.

TDD를 배우는데 있어 도움이 되는 책들.

0%