DateMessageProvider 코드에 대한 두번째 해결 방법

2013-01-11 17:44

"테스트를 모두 성공하려면 어떻게 해야 될까요?"(http://www.slipp.net/questions/50)에에) 대한 두번째 접근 방법을 찾아보자.

"DateMessageProvider 코드에 대한 첫번째 해결 방법 및 이슈 제기"(http://www.slipp.net/questions/51)와와) 같이 문제를 해결할 수 있다. 하지만 모든 경우 이 방법으로 문제를 해결할 수 있는 것이 아니다. 이 글에서도 이슈 제기를 했듯이 새로 구현하는 시점이 아니고 이미 서비스를 오픈해서 운영하지 몇년이 지났으며 DateMessageProvider.getDateMessage() 메소드를 사용하는 부분이 너무 많아 대대적인 변경 작업을 해야한다면 어떻게 될까? 즉, Dependency Injection 개념으로 접근하기 힘든 상황이라면 어떤 방식으로 접근해야 할까? 레거시 코드에 Calendar와 같이 직접적으로 의존관계를 가지는 클래스가 많다면 어떻게 점진적으로 테스트를 할 수 있을까?

두번째 접근 방법은 객체를 직접 생성함으로써 강한 coupling이 발생하는 부분을 별도의 메소드로 추출한 후 이 메소드를 overrride 함으로써 Calendar 인스턴스에 대한 변경이 가능하도록 하는 것이다.

package net.slipp;


import java.util.Calendar;


public class DateMessageProvider {


	public String getDateMessage() {
		Calendar now = createCalendar();
		int hour = now.get(Calendar.HOUR_OF_DAY);


		if (hour < 12) {
			return "오전";
		}
		
		return "오후";
	}
	
	protected Calendar createCalendar() {
		return Calendar.getInstance();
	}
}

먼저 위와 같이 Calendar 인스턴스를 생성하는 createCalendar() 메소드를 extract method 리팩토링을 통해 분리한다. 위와 같이 리팩토링을 하면 Calendar 인스턴스를 다음과 같이 overrider를 통해 테스트 가능하도록 변경할 수 있다.

package net.slipp;


import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;


import java.util.Calendar;


import org.junit.Test;


public class DateMessageProviderTest {
	@Test
	public void 오전() throws Exception {
		DateMessageProvider provider = new DateMessageProvider() {
			@Override
			protected Calendar createCalendar() {
				return createCurrentDate(11);
			}
		};
		assertThat(provider.getDateMessage(), is("오전"));
	}


	@Test
	public void 오후() throws Exception {
		DateMessageProvider provider = new DateMessageProvider() {
			@Override
			protected Calendar createCalendar() {
				return createCurrentDate(13);
			}
		};
		assertThat(provider.getDateMessage(), is("오후"));
	}
	
	private Calendar createCurrentDate(int hourOfDay) {
		Calendar now = Calendar.getInstance();
		now.set(Calendar.HOUR_OF_DAY, hourOfDay);
		return now;
	}
}

첫번째 해결 방법이 인자를 통해 Calendar에 대한 강한 coupling을 제거했다면 이 방법은 메소드를 override 함으로써 Calendar에 대한 coupling을 제거한다. 이 방법은 테스트 코드가 없는 레거시 코드에 점진적으로 리팩토링을 진행할 때 유용하게 사용할 수 있다. 이 예제와 같이 단순한다면 다음과 같이 메소드의 원형을 유지하면서 테스트를 할 수 있을 것이다.

public class DateMessageProvider {
	public String getDateMessage() {
		return getDateMessage(Calendar.getInstance());
	}


	public String getDateMessage(Calendar now) {
		int hour = now.get(Calendar.HOUR_OF_DAY);


		if (hour < 12) {
			return "오전";
		}


		return "오후";
	}
}

하지만 프로젝트를 진행해보면 method 하나의 라인 수도 긴 경우가 많고, method에서 강한 coupling이 발생하는 클래스의 수도 많은 경우가 대부분이다. 이 같은 상황에서 extract method를 통해 강한 coupling 상태에 있는 인스턴스를 분리해 점진적으로 테스트할 수 있다. 이후 테스트 코드가 안정되면 대대적인 리팩토링 작업을 하면 된다.

처음에 정말 작게 시작했는데 많은 분들이 관심을 가져주어 다양한 이야기를 할 수 있었다. 이 글에 대한 해결 방법은 이미 많은 분이 답변을 주셨다.

위 답변들 모두가 이 글과 같은 내용의 의견이라 생각한다.

지금까지 살펴본 두 가지 방법도 좋은 해결 방법이다. 물론 요구사항에 따라 다음 단계가 보일 수도 있고, 그렇지 않을 수도 있지만 지금까지 살펴본 두 개의 방법보다 너 나은 방법이 있지 않을까 생각한다. 이미 답변 중에서도 지금까지의 해결 방법보다 더 깔끔한 해결책을 제시한 분이 계시다. 다음에는 다른 방법을 찾아보자.

0개의 의견 from SLiPP

의견 추가하기

연관태그

← 목록으로