2013-07-17 3 views
2

만약 내가 2 또는 3 차이 소스에서 데이터를 수집하고 결과를 반환하는 코드의 긴 방법이 있습니다. 어떻게 단위를 더 테스트 할 수 있도록 리팩토링 할 수 있습니까? 이 메소드는 웹 서비스이며 모든 데이터를 수집하기 위해 클라이언트 코드에서 한 번의 호출을 만들고 싶습니다.긴 함수의 단위 테스트는 어떻게해야합니까?

일부 부분을 리팩터링하여 더 작은 방법으로 테스트 할 수 있습니다. 그러나 현재의 방법은 여전히이 5 가지 방법을 호출 할 것이고 덜 시험적일 것이다. Java를 프로그래밍 언어로 가정 할 때, 그러한 코드를 테스트 할 수 있도록 만드는 패턴이 있습니까?

답변

4

이것은 매우 일반적인 테스트 문제이며, 이에 대한 가장 일반적인 해결책은 종속성 주입을 사용하여 데이터를 사용하는 코드에서 데이터 소싱을 분리하는 것입니다. 이것은 좋은 테스트를 지원할뿐만 아니라 일반적으로 외부 데이터 소스 (책임의 분리, 통합 지점 분리, 코드 재사용을 촉진하는 이유)와 함께 작업 할 때 좋은 전략입니다.

이 같은 갈 수 있도록해야 할 변경 : 각 데이터 소스에 대한

  • 을 해당 소스의 데이터에 액세스하는 방법을 정의하는 인터페이스를 만든 다음에 데이터를 반환하는 코드를 인수 분해를 이것을 구현하는 별도의 클래스.
  • '긴'함수가 들어있는 클래스에 데이터 소스를 삽입하십시오.
  • 단위 테스트를 위해 각 데이터 소스의 모의 구현을 주입하십시오.

다음은이 코드가 어떻게 보이는지 보여주는 코드 예제입니다.이 코드는 단지 패턴을 설명하는 것일 뿐이지 만 사물에 좀 더 분별있는 이름이 필요합니다. 이 패턴을 연구하고 의존성 주입 & 조롱에 대해 더 배우는 것이 유용 할 것입니다. 단위 테스터 병기에서 가장 강력한 무기 중 두 가지입니다. 긴 방법

public class ClassWithLongMethod { 
    private DataSourceOne dataSourceOne; 
    private DataSourceTwo dataSourceTwo; 

    public ClassWithLongMethod(DataSourceOne dataSourceOne, 
           DataSourceTwo dataSourceTwo) { 
     this.dataSourceOne = dataSourceOne; 
     this.dataSourceTwo = dataSourceTwo; 
    } 

    public Result longMethod() { 
     someData = dataSourceOne.getData(); 
     someMoreData = dataSourceTwo.getData(); 
     ... 
     return result; 
    } 
} 

단위 테스트와

데이터 소스

public interface DataSourceOne { 
    public Data getData(); 
} 

public class DataSourceOneImpl implements DataSourceOne { 
    public Data getData() { 
     ... 
     return data; 
    } 
} 

public interface DataSourceTwo { 
    public Data getData(); 
} 

public class DataSourceTwoImpl implements DataSourceTwo { 
    public Data getData() { 
     ... 
     return data; 
    } 
} 

클래스

import org.junit.Test; 

import static org.mockito.Mockito.mock; 
import static org.mockito.Mockito.when; 

public class ClassWithLongMethodTest { 

    @Test 
    public void testLongMethod() { 

     // Create mocked data sources which return the data required by your test 
     DataSourceOne dataSourceOne = mock(DataSourceOne.class); 
     when(dataSourceOne.getData()).thenReturn(...); 
     DataSourceTwo dataSourceTwo = mock(DataSourceTwo.class); 
     when(dataSourceTwo.getData()).thenReturn(...); 

     // Create the object under test using the mocked data sources 
     ClassWithLongMethod sut = new ClassWithLongMethod(dataSourceOne, 
                  dataSourceTwo); 

     // Now you can unit test the long method in isolation from it's dependencies 
     Result result = sut.longMethod(); 

     // Assertions on result 
     ... 
    } 
} 

구문상의 실수를 용서해주십시오. (요즘은 많이 쓰지 않습니다.)

0

"큰"방법에 대한 테스트는 작은 방법을 조롱 할 수있는 통합 테스트처럼 보일 것입니다.

"큰"방법을 5 개의 분리 된 방법으로 분리 할 수있는 경우 "큰"방법을 의미있는/고립 된 방법으로 의미있는 그룹으로 더 분할 할 수 있습니다.

그런 다음 "큰"방법에 대해 격리 된 방법의 큰 그룹을 조롱 할 수 있습니다.

+0

그렇다면 복잡성을 소개하지 않습니까? 함수가 2 ~ 3 번 이상 사용되지 않으면 인라인이라면 더 간단하지 않습니까? –

+0

@JakeZieve \t 전적으로 다릅니다.당신은 한 종류의 복잡성을 다른 것과 비교해서 거래하고 있습니다. 하나의 방법으로 모두 복잡성을 증가 시키면, 순환 적 복잡성이 더 커지고, 추론하기가 더 어렵고, 테스트하기가 더 어려워집니다. 나는 편성 할 수있는 맛있는, 한입 크기의 덩어리를 선호한다. 리팩토링은 재사용을위한 것이 아니라 추론 능력을 향상시키는 것입니다. 나는 대부분의 함수 추출이 (처음에는) 재사용에 관한 것이지만, 단일 메서드에서 추상화의 수준을 평평하게하고 염려를 분리한다고 주장한다. –

+0

그러나 당신은 추상화 단계를 평평하게하지 않고, 추상화를 도입하고 있습니다. 맞습니까? 3 개의 다른 파일에서 3 가지 다른 메소드로 메소드를 리팩토링하면 원래 메소드에 대해 추론해야하는 2 개의 추가 경로가 있습니다. 순환 적 복잡성이 증가하지 않습니까? "주저하는 것"에 대해 "추론하기가 더 힘듭니다"? 여전히 동일한 코드를 읽어야합니다. 하나가 아닌 3 개의 다른 장소에서 읽어야합니다. 게다가 그 함수에 대해 멋진 이름을 생각해 내야합니다. 오버 헤드가 추가되지 않습니까? 논리 블록 블록이 무엇이고 왜, 왜 같은 결과를 얻을 수 있는지를 설명하는 간단한 설명입니다. –