본문 바로가기

IT/자바

자바 플레이그라운드 with TDD, 클린코드 강좌 후기 Day 3

반응형

대표적으로 테스트하기 어려운 코드

1. 내부 API : Random, Shuffle, 날짜

2. 외부 세계 : 외부 Rest API, 데이터베이스 API

 

테스트 힘든 코드 리펙토링 방법 

테스트하기 힘든 코드로 랜덤 값으로 자동자 이동에 대한 단위테스트 케이스를 예시로 보면서 어떻게 작성함으로써 테스트 코드 작성이 가능한지 설명해주셨다. 

 

먼저 아래와 같은 코드는 테스트를 실행할때마다 다른 랜덤 값으로 실행됨으로 결과를 예측하기 어렵고 테스트가 성공한게 맞는지 판단하기 어렵다 

 

다음과 같은 방법으로 코드를 개선할 수 있다. 

1. Car 클래스의 move 메소드를 protected 접근자 제한으로 변경

 

아래와 같이 test 클래스에서 move 메소드를 오버라이딩함으로써 테스트가 가능하다.

랜덤 값 생성 메소드
오버라이딩한 테스트코드

2. Car 클래스의 move 메소드에서 사용하는 랜덤값을 상위로드에서 인자로 받아 처리하도록 개선

 

랜덤값을 GameController클래스에서 생성한뒤 Car 클래스로 넘겨 받음으로 move 메소드에 대한 테스트가 가능하다.

랜덤값을 인자로 받는 move 메소드
인자로 받는 move 메소드 테스트코드

3. Car 클래스의 move 메소드 요구사항이 자주 변경되는 경우 별도의 인터페이스로 분리

인터페이스 메소드 구현 클래스

 

MoveingStrategy 인터페이스를 통해 테스트클래스에서 해당 인터페이스 오버라이딩함으로써 테스트가 가능하다.

인터페이스로 분리 후 작성된 테스트코드

* 인터페이스의 구현 메소드가 1개인 경우 람다를 통해 간결하게 코드를 변경할 수 있다. (java 8의 익명 메소드 사용)

이렇게 3가지 방법으로 테스트하기 어려운 코드를 쉽게 바꿀 수 있다. 

각 케이스에 맞게 적절한 방법을 선택해서 사용하면 된다.

 

클래스 분리를 잘하기 위해서는 아래의 원칙을 꼭 기억해야 된다.

 

클래스 분리를 위한 원칙

1. 모든 원시값과 문자열 포장

모든 원시값을 포장한다는 의미는 모든 값을 의미있는 객체로 포장하는 걸 뜻한다. 

 

예를 들어 Car의 원시값인 int position을 의미있는 Position 객체로 포장할 수 있다. 

* 도메인 객체에서 Setter 메소드는 쓰지말고 Getter 메소드 역시 가능한 사용하지 마라... (단, 객체를 전달하는 DTO에서는 사용해도 됨)

-> Setter 대신 꼭 객체한테 메세지를 보냄으로써 로직 구현 역할을 도메인한테 위임하는 것이 좋다.

 

 

이렇게 원시값을 포장함으로써 외부에서 해당 객체를 임의로 변경할 수 없는 완전한 객체 형태를 보장하게 된다. (= valueObject)

(move 메소드도 본인의 값을 바꾸는 것이 아닌 새로운 객체를 생성하는 방식임)

 

단 이렇게 매번 객체로 값을 포장할 경우 인스턴스가 늘어나게 되고 가비지 컬렉션이 발생함으로 성능 저하를 일으킬 수 있다.

그러므로 개인적인 의견으론 불변을 보장하기 보단 가변 상태로 외부에서 함부로 값을 수정할 수만 없게 적용해주는 것이 좋다고 생각한다. 

 

 

2. 일급콜렉션

 

List 와 같은 콜렉션이 나오면 원시값 포장처럼 객체로 포자하는걸 의미한다. 

여기서 일급 콜렉션은 콜렉션 변수를 갖은 인스턴스가 1개임을 의미하는 것이다.

이렇게 포장하는 이유는 원시값처럼 외부에서 함부로 데이터 변경되는 경우를 막고 해당 컬렉션의 데이터는 보장하기 위함이다.

반응형