TDD 컨퍼런스 후기

1. 테스트하기 쉬운 코드로 개발하기

테스트하기 쉬운 코드의 2가지 원칙

  • 같은 입력에 항상 같은 결과를 반환하는 코드 = Deterministic(결정적인)
  • 외부 상태를 변경하지 않는 코드 = No side effect(부수효과가 없음)

  • 테스트하기 어려운 코드

    LocalDateTime.now() db 조회 외부 상태를 변경하는 코드(db/file write 등)

테스트 대상이 되는 코드가 테스트하기 쉬운 코드와 테스트하기 어려운 코드가 섞여있다면, 분리하라.

  • 분리하면서 테스트 하기 어려운 코드의 로직이 주는 결과를 테스트하기 쉬운 코드의 파라미터로 넘겨줘
  • 테스트하기 쉬운 코드와 테스트하기 어려운 코드는 어디서 만나야 하나?

    최대한 가장자리에서 만나야 한다. (예외: 로깅, facade 패턴 ?)

예제 코드 : 컨퍼런스 등록하기

  1. 유효성 검사
  2. 이미 등록된 좌석수 DB에서 읽어오기
[Fact]
public void 이메일에_골뱅이가_없으면_유효하지않는_형식입니다()
{
    // Arrange
    var sut = new ConferenceRegistration{Email = "terry.lee@gmail.com"};

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

    // Assult
}

class ConferenceRegistration {
    // ...
    string Validate() {
        return "이메일은 @를 포함해야합니다.";
    }

}

유효성 검사에 대해 더 많은 테스트 케이스가 있음. (본 세미나에선 생략)

(이후로 세미나에 집중해서 들었음 - 코드가 많아서)

행위 검증(Mock 사용) vs 상태 검증(Mock 사용X)

-> Mock을 남발할 가능성이 크다. => 상태 검증으로 돌아가보자.

http://jwchung.github.io/testing-oh-my

2. 의식적인 연습으로 TDD, 리팩토링 연습하기

추천 책 : 1만 시간의 재발견

  • 목적 의식 있는 연습

3. 코드 품질을 위한 테스트 주도 개발

코드 품질 측면에서 TDD를 간단하게 살펴보고, TDD가 소프트웨어의 품질을 어떻게 높일 수 있는지 알아봅니다.
또한 TDD 적용에 따른 트레이드 오프(trade-off)와 이에 대한 새로운 대안으로 BDD(Behavior-Driven Development) 적용을 고민해보는 시간을 갖습니다.

https://github.com/RedCA-Family/code-analyst : 삼성 SDS 에서 만든 오픈소스 코드 분석 툴

4. 테알못 신입은 어떻게 테스트를 시작했을까?

TDD를 실패하게 만드는 테스트들

불필요한 테스트

  • 비즈니스와 관련된 버그를 낼 가능성이 낮거나 없거나,
  • 테스트를 유지함으로서 얻는 이익 < 테스트 유지와 관리에 드는 비용일 때,
  • 테스트가 단언하고 있는 내용이 사용자에게 중요한 가치를 주는 것이 아닐 때.
  • ==> 테스트를 하지 않는다.

필요하지만 검증 방식이 잘못된 테스트

  • 특정 위치를 가지고 데이터를 갖고 있는지 확인하는 게 아니라 데이터가 위치와 상관없이 의도한 대로 있는지만 검사한다.

검증력이 떨어지는 테스트

테스트 제목과 검증의 불일치

테스트를 앞서가는 프로덕션 코드

  • TDD 원칙 : 테스트가 성공할 만큼만 최소한의 코드만 작성한다. 를 깨버리게 됨

고민되는 부분

  • Fixture : 테스트를 하기 위해 만든 데이터
  • 낮은 추상화 수준
  • 높은 응집, 낮은 결합을 달성해야 함. 함수 분리로 낮은 결합은 달성했지만 높은 응집을 달성하진 못함

5. 테스트를 돌보기 위한 매우 간단한 실천 방법들, 그리고 효과

  • 테스트 코드는 프로젝트가 사용되는 최초의 장소이며 고객이다.
  • 모든 역사는 테스트코드가 시작이다.

추상이란?

  • 문맥 위에서 오직 관심 있는 것들에 대해서만 집중하여 명확하게 하는 것

    1 + 12 의 결과는 13 이어야만 하나? 1이 맞을 수도 있다. (시간, 진수)

숨겨진 본질

  • 낮은 추상화
  • 들쭉날쭉한 추상화
  • 끊어진 논리
  • 알 수 없는 의도

욕심쟁이

  • 테스트가 실패하는 이유는 단 하나여야 한다.
  • 하나의 테스트는 오직 한가지만 똑바로 검사해야한다.

    => SRP of SOLID principals

인지능력의 과부하

  • 흩어진 코드와 데이터
  • 매직넘버

깨지기 쉬운 것들

  • 높은 결함
  • 낮은 응집

쿠팡에서 실제 프로덕션 코드를 보여줬음

  • https://github.com/yangwansu/okkycon2018.git

6. 당신들의 TDD가 실패하는 이유 (Live Coding)

TDD, 왜 실패하나

  • 준비가 안 되서

모든 것에 TDD를 하지 않는다.

  1. 우리가 보호해야 하는 것
    • AWS?
    • Spring?
    • 도메인
  2. 우리가 제어할 수 없는 것
    • 외부 세상
    • 실 세계
    • 인프라
    • 외부 서비스
    • 레거시
  3. 테스트 대상 코드의 구현(Implementation)을 테스트하면 안 됨
    • Mock 을 사용한 테스트들의 부작용
      • Mock 을 사용했단 것 자체가, 어떻게 테스트 대상 코드가 구현되었는지에 관심을 가지고 있다는 게 되버림
  4. 구현을 테스트하는 게 아니라 설계(Interface)를 테스트 해야 함
  • 구현과 인터페이스의 경계를 구분하는 것은 Kent Beck 아저씨도 어렵다고 한다.

정보 숨김(Information Hiding)의 원칙

  • 어려운 설계 결정과 변경될 가능성이 높은 설계 결정들을 다른 모듈로부터 숨기는 것.
  1. 레거시와 함께 살기
    • 레거시 코드를 래핑하는 어댑터 레이어를 구현한다.
    • 레거시 코드는 수동으로 아주 간단한 형태의 테스트를 만들어 돌려본다.
  2. 프로세스
    • 점진
    • 반복
    • Fail-fast
  3. 반복주기
    • 계획
    • 실행
    • 평가
  • 계획이 가장 중요함
  1. 문화
    • 공유
      • 목표
      • 지식
  2. 아키텍쳐
    • 낮은 결합
    • 높은 응집
    • 도메인 모델 보호
    • CQRS (이건 내가 여기다 적은 거)
  3. 도메인 모델과 플랫폼
    • 자바 개발자들은 도메인 모델을 스프링이란 플랫폼에 맡겨버리는 실수를 본인도 모르게 저지르곤 한다.
    • 플랫폼으로부터 우리가 보호해야할 도메인 모델을 분리해야할 필요가 있다.
    • Business Logic 에 RDB, Kafka 등 플랫폼을 연관 짓지 말라. => 플랫폼으로 도메인 모델을 오염/감염시키지 말라.
  4. 라이브 코딩 조금

결론

  1. 왜 내가 TDD를 실패하는 지에 대한 인사이트를 조금 얻었다.
  2. 하지만 개인적으로 약 8시간 동안 진행된 구성 치고는 얻은 게 별로 없었다.
  3. (개인적으로) 의외로 나랑 비슷한 레벨/연차라서 그런지 주니어 개발자 연사 분의 발표에서 배운 게 좀 있었던 것 같다.
  4. 지난번 데뷰도 그렇고 이번 컨퍼런스도 그렇고, 컨퍼런스는 나랑 안 맞는 것 같다.
    • 그냥 책으로 공부하는 게 나에게 더 효율적인 방법인 것 같다.
    • 인터넷 자료도 좋고.

Comments