토비의 스프링 | 6장 AOP - 더 나은 설계, 테스트

주싱·2022년 10월 24일
0

토비의 스프링

목록 보기
22/30
post-custom-banner

토비의 스프링 6장 AOP를 읽고 더 나은 설계 및 테스트를 위한 내용을 별도로 정리합니다.

1. 더 나은 설계

기술영역에서의 변하는 것과 변하지 않는 것

스프링의 ProxyFactoryBean의 설계를 보면서 기술적인 영역에서도 변하는 것과 변하지 않는 것을 구분하면 재사용성을 높일 수 있고 최소한의 노력으로 변경에 대처할 수 있음을 본다.

관심사를 분리하는 패턴

  • 코드 덩어리에서 성격이 다른 코드 분류 (서로 주고 받는 정보가 없다면 완전히 분리 가능)
  • 메서드로 분리
  • 클래스로 다시 분리
  • 인터페이스 정의
  • 의존 클래스를 인터페이스로 참조하고, 런타임에 외부에서 주입받도록 변경

부가적인 작업과 위임

클라이언트 객체가 서비스 객체를 인터페이스를 통해 의존하고 있고, DI를 통해 의존관계 주입을 받고 있다면 클라이언트와 서비스 객체는 변경 없이 서비스에 부가적인 작업을 추가할 수 있다. 방법은 부가작업 객체가 서비스 객체가 구현한 인터페이스를 동일하게 구현하고 각각의 인터페이스에 부가 작업을 구현한 다음 서비스 객체에 기존 서비스 작업은 위임하는 방법을 사용하는 것이다.

2. 더 나은 테스트

작은 단위의 테스트가 좋은 이유

  • 테스트의 단위가 작으면 테스트가 실패했을 때 그 원인을 찾기 쉽다. 반대로 오류가 발견됐을 때 테스트가 진행되는 동안 실행된 코드의 양이 많다면 그 원인을 찾기가 매우 힘들어질 수 있다. 클래스 하나가 동작하도록 테스트를 만드는 것과 클래스 수십 개가 얽히고 설켜서 동작하도록 만드는 것 중에 어떤 것이 논리적인 오류를 찾기 쉬울지는 분명하다.
  • 테스트 단위가 작으면 테스트의 의도나 내용이 분명해지고, 만들기도 쉬워진다. 반대로 테스트할 대상이 크고 복잡하면 테스트를 만들기도 그만큼 어렵다.
  • 테스트 단위가 크면 충분히 테스트 하는 코드를 만들지 못할 수 있다.
  • 작은 단위로 테스트하면서 개발을 진행하면 나중에 덩치가 커져도 어렵지 않게 오류를 찾을 수 있다. 왜냐하면 작은 단위의 테스트로 검증한 부분은 제외하고 문제에 접근해 볼 수 있기 때문이다.

작은 단위 테스트를 어렵게 하는 요소

작은 단위로 테스트하고 싶어도 그럴 수 없는 경우가 많다. 테스트 대상이 다른 오브젝트와 환경에 의존하고 있다면 작은 단위의 테스트가 주는 장점을 얻기 힘들다.

복잡한 의존관계 속의 테스트

  • 객체들 간의 복잡한 의존관계로 인해 하나의 객체가 테스트 대상인 것 처럼 보이지만 사실은 그 뒤의 의존관계를 따라 등장하는 오브젝트와 서비스, 환경 등이 모두 합쳐져 테스트 대상이 되는 것이다.
  • 이렇게 되면 실제 테스트 대상 객체를 테스트하는 도중에 다른 의존 객체나 환경의 문제로 테스트가 실패하는 일이 생기고 그 원인을 찾느라 불필요한 시간을 낭비해야 할 수도 있다.
  • 그래서 테스트 대상이 환경이나, 외부 서버, 다른 클래스의 코드에 종속되지 않도록 고립시킬 필요가 있다.

테스트 고립을 위해 Mock 객체를 도입하는 과정

  • 테스트 대상 객체와 의존 객체가 어떤 정보를 주고 받는지 입출력 내역을 먼저 확인한다.
  • 그리고 의존 객체의 Mock객체를 만든다면 단순히 테스트 대상의 출력을 받아주기만 하면 되는지, 아니면 특정한 입력을 반환해 주어야 하는지, 특정한 예외를 만들어 줘야 하는지, 테스트 대상 객체의 출력 값을 검증해야하는지 등을 삺펴본다.
  • 그리고 의존 객체 Mock 객체에서 테스트 대상 객체가 사용하지 않는 메서드는 UnsupportedOperationException을 던져 주도록 하는 것이 좋다.

스프링 컨테이너 없는 단위 테스트

하나의 객체를 단위로 테스트할 수 있도록 고립시켜두면 스프링 컨테이너의 도움 없이 테스트를 실행할 수 있다.

Mock 프레임워크로 할 수 있는 일

  • 스프링 빈이 아닌 경우에도 Mock 객체로 생성 가능하다. Mockito.mock()
  • 인터페이스만으로 Mock 객체 생성이 가능하다.
  • 단순히 메서드 호출을 받는다.
  • 메서드의 반환 값을 모의한다.
  • 메서드 실행 중 예외를 모의한다.
  • 메서드 호출 횟수를 검증한다.
  • 메서드로 전달된 파라미터를 수집해서 검증한다.

테스트 코드가 아니더라도

테스트 코드를 직접 작성하지 않더라도 코드를 간단히 조작해보며 프레임워크나 우리가 작성한 코드의 기능을 검증해 보는 건 좋은 습관이다.

트랜잭션 테스트와 비 트랙잭션 테스트 나누기

트랜잭션 테스트와 비 트랜잭션 테스트를 아예 클래스를 구분해서 만들도록 권장한다.

단위 테스트와 통합 테스트 분리

일반적으로 의존, 협력 객체를 사용하지 않고 고립된 상태에서 테스트를 진행하는 단위 테스트와 DB 같은 외부 리소스나 여러 계층의 클래스가 참여하는 통합 테스트는 아예 클래스를 구분해서 따로 만드는게 좋다.

코드가 바뀌지 않는 한

코드가 바뀌지 않는 한 어떤 순서로 진행되더라도 테스트는 일정한 결과를 내야 한다.

profile
소프트웨어 엔지니어, 일상
post-custom-banner

0개의 댓글