BottleH Blog

Unit Testing - 10장 데이터베이스 테스트

    Tags

  • Test
Unit Testing - 10장 데이터베이스 테스트 thumbnail

목에 대해 리팩터링 내성과 회귀 방지를 최대화해서 최대 가치의 통합 테스트를 개발하는 데 도움이 되는 지침을 알아본다.

📖 10.1 데이터베이스 테스트를 위한 전제 조건


🔖 10.1.1 데이터베이스를 형상 관리 시스템에 유지

데이터베이스 스키마를 일반 코드로 취급하는 것이 첫 번째 단계다

  • 일반 코드와 마찬가지로 형상 관리 시스템에 저장하는 것이 최선

모델 데이터베이스를 사용하는 것은 데이터베이스 스키마를 유지하는 데 상당히 좋지 못한 방법이다.

  • 변경 내역 부재
  • 복수의 원천 정보

🔖 10.1.2 참조 데이터도 데이터베이스 스키마다

애플리케이션이 데이터를 수정할 수 있으면 일반 데이터고, 그렇지 않으면 참조 데이터다.

참조 데이터는 애플리케이션의 필수 사항이므로, 테이블, 뷰 그리고 다른 데이터베이스 스키마와 함께 형상관리 시스템에 저장해야 한다.

🔖 10.1.3 모든 개발자를 위한 별도의 데이터베이스 인스턴스

공유 데이터베이스를 사용하면 개발 프로세스를 방해하게 된다.

  • 서로 다른 개발자가 실행한 테스트는 서로 간섭되기 때문이다.
  • 하위 호환성이 없는 변경으로 다른 개발자의 작업을 막을 수 있기 때문이다.

🔖 10.1.4 상태 기반 데이터베이스 배포와 마이그레이션 기반 데이터베이스 배포

  • 상태 기반 방식은 상태를 형상 관리에 저장함으로써 상태를 명시하고 비교 도구가 마이그레이션을 암묵적으로 제어할 수 있게 한다.
  • 마이그레이션 기반 방식은 마이그레이션을 명시적으로 하지만 상태를 암묵적으로 둔다. 데이터베이스 상태를 직접 볼 수 없으며 마이그레이션으로 조합해야 한다.

📖 10.2 데이터베이스 트랜잭션 관리


🔖 10.2.1 제품 코드에서 데이터베이스 트랜잭션 관리하기

잠재적인 모순을 피하려면 결정 유형을 두 가지로 나눠야 한다.

  • 업데이트할 데이터
  • 업데이트 유지 또는 롤백 여부

Database 클래스를 리포지터리와 트랜잭션으로 나눠서 이러한 책임을 구분할 수 있다.

  • 리포지터리는 데이터베이스의 데이터에 대한 접근과 수정을 가능하게 하는 클래스
  • 트랜잭션은 데이터 업데이트를 완전히 커밋하거나 롤백하는 클래스

🔖 10.2.2 통합 테스트에서 데이터베이스 트랜잭션 관리하기

통합 테스트에서 테스트 구절 간에 데이터베이스 트랜잭션이나 작업 단위를 재사용하지 말라.

  • 통합 테스트에서 적어도 세 개의 트랜잭션 또는 작업 단위를 사용하라
    • 준비, 실행, 검증 구절당 하나씩

📖 10.3 테스트 데이터 생명 주기


🔖 10.3.1 병렬 테스트 실행과 순차적 테스트 실행

통합 테스트를 병렬로 실행하려면 상당한 노력이 필요하다.

  • 성능 향상을 위해 시간을 허비하지 말고 순차적으로 통합 테스트를 실행하는 것이 더 실용적이다.
  • 통합 테스트의 실행 시간을 최소화해야 하는 경우가 아니라면 컨테이너를 사용하지 않는 것이 좋다.

🔖 10.3.2 테스트 실행 간 데이터 정리

테스트 실행 간 데이터를 정리하는 방법은 네 가지가 있다.

  • 각 테스트 전에 데이터베이스 백업 복원하기
  • 테스트 종료 시점에 데이터 정리하기
  • 데이터베이스 트랜잭션에 각 테스트를 래핑하고 커밋하지 않기
  • 테스트 시작 시점에 데이터 정리하기
    • 이 방법이 가장 좋다.

🔖 10.3.3 인메모리 데이터베이스 피하기

인메모리 DB의 장점

  • 테스트 데이터를 제거할 필요가 없음
  • 작업 속도 향상
  • 테스트가 실행될 때마다 인스턴스화 가능

위와 같은 장점에도 불구하고, 기능적으로 일관성이 없기 때문에 사용하지 않는 것이 좋다.

📖 10.4 테스트 구절에서 코드 재사용하기


통합 테스트를 짧게 하기에 가장 좋은 방법은 비즈니스와 관련이 없는 기술적인 부분을 비공개 메서드나 헬퍼 클래스로 추출하는 것

🔖 10.4.1 준비 구절에서 코드 재사용하기

테스트 준비 구절 간에 코드를 재사용하기에 가장 좋은 방법은 비공개 팩토리 메서드를 도입하는 것

  • 기본값을 사용하면 인수를 선택적으로 지정
  • 선택적 인수를 사용하면 어떤 인수가 테스트 시나리오와 관련이 있는지도 강조할 수 있음.

팩토리 메서드의 위치는 동일한 클래스에 배치

  • 코드 복제가 중요한 문제가 될 경우에만 별도의 헬퍼 클래스로 이동
  • 기초 클래스에 팩토리 메서드를 넣지 말라.
    • 기초 클래스는 데이터 정리와 같이 모든 테스트에서 실행해야 하는 코드를 위한 클래스

🔖 10.4.2 실행 구절에서 코드 재사용하기

decorator 메서드를 사용하면 테스트의 실행 구절은 몇 줄만으로 충분하다.

  • 어떤 컨트롤러 기능을 호출해야 하는지에 대한 정보가 있는 대리자(delegate)를 받는 메서드를 도입

🔖 10.4.3 검증 구절에서 코드 재사용하기

  • 헬퍼 메서드 사용
  • 플루언트 인터페이스 활용

🔖 10.4.4 테스트가 데이터베이스 트랜잭션을 너무 많이 생성하는가?

유지 보수성을 위해 성능을 양보함으로써 절충하는 것이 좋다.

  • 빠른 피드백유지 보수성 간의 절충

📖 10.5 데이터베이스 테스트에 대한 일반적인 질문


🔖 10.5.1 읽기 테스트를 해야 하는가?

쓰기는 위험성이 높기 때문에 철저히 테스트하는 것이 매우 중요하다.

그러나 읽기는 가장 복잡하거나 중요한 읽기 작업만 테스트하고, 나머지는 무시하라.

  • 읽기를 테스트하는 경우에는 성능 면에서 ORM보다 우수한 일반 SQL을 사용하는 것이 좋다

🔖 10.5.2 리포지터리 테스트를 해야 하는가?

리포지터리는 복잡도가 거의 없고 프로세스 외부 의존성인 데이터베이스와 통신한다.

  • 외부 의존성이 있으므로 유지비⬆️
  • 테스트하기 가장 좋은 방법은 약간의 복잡도를 별도의 알고리즘으로 추춭하고 해당 알고리즘 전용 테스트를 작성

리포지터리는 직접 테스트하지 말고, 포괄적인 통합 테스트 스위트의 일부로 취급하라.

📖 10.6 결론


데이터베이스 테스트를 잘 만들면 버그로부터 훌륭히 보호할 수 있다.

  • 특히, ORM 전환 혹은 DB 공급업체 변경에 큰 도움이 된다.
Written by@BottleH
Back-End Developer

GitHub