Unit Testing

단위 테스트작성 스타일과 함수형 어키텍처

25G 2024. 2. 24. 12:20

단위 테스트의 세가지 스타일

  • 출력 기반 테스트
  • 상태 기반 테스트
  • 통신 기반 테스트

위 스타일은 하나의 테스트에서 둘 이상 또는 세가지를 다 사용할 수 있다.

출력기반 테스트

테스트 대상 시스템에 입력을 넣고 생성되는 출력을 점검 하는 방식. 전역 상태나 내부 상태를 변경하지 않는 코드에만 적용되므로 반환 값만 검증하면 된다.
간단하게 출력값을 검증을 기반으로 하는 테스트, 이는 함수형이라고도 하고 사이드 이펙트 없는 코드 선호를 강조하는 프로그래밍 방식인 함수형 프로그래밍의 뿌리를 두고있다.

상태 기반 스타일 정의

상태 기반 스타일은 작업이 완료된 후 시스템 상태를 확인하는 것이다. 이 테스트 스타일에서 상태라는 용어는 테스트 대상 시스템이나 협력자 혹은 db나 파일시스템과 같은 프로세스 외부 의존성의 상태를 의미할 수 있다. 작업이 완료된 후 시스템의 최종 상태를 검증한다.

통신 기반 스타일 정의

목을 사용해 테스트 대상 시스템과 협력자 간의 통신을 검증한다. ex) smtp service

단위 테스트 스타일 비교

이전 포스팅한 좋은 단위테스트의 4대 요소 회귀 방지, 리팩터링 내성, 빠른 피드백, 유지 보수성이 있다. 여기서 회귀방지와 피드백 속도 측면에서 세가지 스타일을 비교한다.

피드벡 속도 지표와 스타일의 비교

조금만 생각해 보면 테스트 스타일과 테스트 피드백 속도와는 전혀 관련이 없다. 코드 양, 복잡도와 스타일과는 연관관계가 없기때문

리팩터링 내성 지표와 스타일 비교

출력 기반 테스트는 대상 메서드에만 결합되니 거짓양성 방지가 스타일중 가장 우수하다.

상태 기반 테스트는 일반적으로 거짓 양성이 되기 쉽다. 상태 검증하려 하다보면 구현 세부사항을 테스트할 확률이 올라가기 때문.

통신 기반 테스트가 가장 허위 경보에 취약한 스타일이다. 테스트 대역으로 상호 작용을 확인하는 테스트는 대부분 깨지기 쉽다. 항상 스텁과 상호 작용하는 경에는 상호작용을 확인해서는 안된다. 애플리케이션 경계를 넘는 상호작용을 확인하고 해당 상호 작용의 사이드 이펙트가 외부 환경에 보이는 경우에만 목이 괜찮다.

유지 보수성 지표로 스타일 비교

해당 지표는 테스트 스타일과 관련이 많은데에 반해 이를 완화할 수 있는 방법이 많지않다. 왜냐하면 비용을 측정하는 부분이 테스트를 이해하기 얼마나 어려운지? 얼마나 실행하기 어려운지? 로 결정나기 때문.

출력 기반 테스트의 유지 보수성

출력기반 테스트 스타일이 유지보수하기 가장 용이하다. 왜냐하면 전역 상태나 내부 상태를 변경할 리가 없기때문.

상태 기반 테스트의 유지보수성

상태 검증을 위해서는 출력 검증보다 더 많은 공간을 차지하기때문에 출력기반보단 좀 떨어진다.

통신 기반 테스트의 유지 보수성

통신을 검증하려면 상태도 체크해야하고 테스트대역과 상호 검증도 해야하며 어러므로 코드가 복잡해지기 마련이기때문에 세 스타일중 가장 유지보수성 지표와 멀어지게된다.

함수형 아키텍처

함수형 프로그래밍은 수학적 함수를 사용한 프로그래밍이다. 수학적 함수의 입출력은 메서드 이름, 인수, 반환 타입으로 구성된 메서드 시그니처에 명시해야하다.
즉 입력과 출력이 있고 둘 다 메서드 시그니처에 명시돼 있다. f(x) = x + 1

이러한 형태의 특징 때문에 출력기반 스타일과 가장 적합하다 이는 유지보수성이 뛰어나고 거짓 양성 빈도가 낮아진다. 하지만 숨은 입출력 코드를 테스트하기 힘들게 한다. 사이드 이팩트, 예외, 내외부 상태에 대한 참조등은 출력기반스타일에서 테스트 하기가 힘들어진다.

메서드가 수학적 함수인지 판별하는 가장 좋은 방법은 프로그램의 동작을 변경하지 않고 해당 메서드에 대한 호출을 반환 값으로 대체할 수 있는지 확인하는 것이다. 메서드 호출을 해당 값으로 바꾸는 것을 참조 투명성 이라고한다.

그래서 함수형 아키텍처가 뭐지?

사이드 이펙트는 사용자 정보 업데이트 같은 이팩트 들이고 함수형 프로그래밍의 목표는 사이드 이펙트를 완전히 제거하는 것이 아니라 비즈니스 로직을 처리하는 코드와 사이드 이펙트를 일으킨 코드를 분리하는 것이다. 이를 모두 고려하면 복잡도가 커지고 유지보수성이 떨어진다. 함수형 아키텍처는 바로 이곳에 적용돼서 사이트 이팩트를 비즈니스 연산 끝으로 몰아서 비즈니스 로직을 사이드 이펙트와 분리한다.
즉 함수형 아키텍처는 사이드 이펙트를 다루는 코드는 최소화하면서 순수 함수 방식으로 작성한 코드의 양을 그대화 한다. 즉 일단 깨체가 생성되면 그 상태는 바꿀 수 없다. 이는 생성 후 변경 가능한 객체와 대조적이다.
이러한 아키텍처는 비즈니스 로직과 사이드 이펙트를 분리한다.

  • 결정을 내리는 코드: 사이드 이펙트가 필요 없기 때문에 수학적 함수를 사용해 작성할 수 있다. -> 함수형 코어
  • 해당 결정에 따라 작용하는 코드: 수학적 함수에 의해 이뤄진 모든 결정을 데이터베이스의 변경이나 메시지 버스로 전송된 메시지와 같이 가시적인 부분으로 변환한다. -> 가변 셸

위 둘과의 협력 관계

  • 가변 셸은 모든 입력을 수집한다.
  • 함수형 코어는 결정을 생성한다.
  • 셸은 결정을 사이드 이펙트로 변환한다.

성능 단점

출력 기반 테스트만큼 빠르게 작동한다. 이제 시스템은 프로세스 외부 의존성을 더 많이 호출하고, 그 결과로 성능이 떨어진다.
코드베이스의 크기도 커진다. 함수형코어와 가변 셸 사이를 명확하게 분리해야 한다. 궁극적으로 코드 복잡도가 낮아지고 유지 보수성이 향상되지만 초기에 코딩이 더 픽요하다. 함수형 방식에서 순수성에 많은 비용이 든다면 순수성을 따르지 않는것도 좋은 선택지가 될 수 있다. 대부분의 모든 도메인 모델은 불변을 ㅗ할 수 없기때문에 출력 기반 테스트에만 의존할 수 없다.