2011-02-17 2 views
9

요청 개체에서 채우는 DTO가 있는데 요청 개체에 많은 필드가 있습니다. populateDTO() 메서드가 올바른 위치에 값을 넣는 지 여부를 확인하는 테스트를 작성하고 싶습니다. 테스트 당 하나의 주장 규칙을 따르면 각 필드를 테스트하기 위해 많은 수의 테스트를 작성해야합니다. 다른 접근법은 단일 테스트에 여러 개의 어설 션을 작성하는 것입니다. 테스트 규칙 당 하나의 주장을 따르는 것이 정말로 권장 되는가 아니면 이러한 경우에 진정할 수 있는가? 이 문제에 어떻게 접근합니까?JUnit 테스트에서 다중 어설 션을 피하는 방법은 무엇입니까?

답변

0

해당 규칙이 루프 상태로 확장 되었습니까? 이 점을 고려하십시오

Collection expectedValues = // populate expected values 
populateDTO(); 
for(DTO dto : myDtoContainer) 
    assert_equal(dto, expectedValues.get(someIndexRelatedToDto)) 

정확한 구문은별로 중요하지 않지만 지금은 내가 바라 보는 개념입니다.

편집 : 코멘트 후 ...

대답은 ... 아니!

원칙이 존재하는 이유는 개체의 어떤 부분이 실패했는지 식별 할 수 있기 때문입니다. 당신이 한 가지 방법으로 그것들을 가지고 있다면, 당신은 단 한가지 단언으로, 그 다음으로, 다음으로, 당신은 그들 모두를 보지 못할 것입니다.

그래서 당신은 한 두 가지 방법으로 할 수 있습니다 :

  1. 한 가지 방법, 적은 상용구 코드를.
  2. 많은 방법, 테스트 실행

그것은 당신에게 달려 있습니다에 대한 더 나은보고, 모두 기복이있다. 3. 목록 항목

+0

DTO는 POJO이고 요청도 POJO입니다. assertEqual ("abc", dto.getName()); 등등. – Ravi

+0

그냥'assert (dto.equals (mockObject)')하는 것을 피하기를 원한다.원리 당 1을 위반하지 않고 각각의 개별 요소를 주장하고 싶습니까? – corsiKa

+0

그래, 원칙에 어긋나지 않으면 서 각각의 개별 요소를 주장하고 싶다. JAXB에서 생성 된 객체가 요청입니다. – Ravi

1

parameterized test의 경우 첫 번째 매개 변수는 propertyname이고 두 번째 매개 변수는 예상 값입니다.

-1

또는 해결 방법을 사용할 수 있습니다.

import junit.framework.Assert; 
import org.junit.After; 
import org.junit.AfterClass; 
import org.junit.Before; 
import org.junit.BeforeClass; 
import org.junit.Test; 

public class NewEmptyJUnitTest { 

    public NewEmptyJUnitTest() { 
    } 

    @BeforeClass 
    public static void setUpClass() throws Exception { 
    } 

    @AfterClass 
    public static void tearDownClass() throws Exception { 
    } 

    @Before 
    public void setUp() { 
    } 

    @After 
    public void tearDown() { 
    } 


    @Test 
    public void checkMultipleValues() { 
     String errMessages = new String(); 

     try{ 
      this.checkProperty1("someActualResult", "someExpectedResult"); 
     } catch (Exception e){ 
      errMessages += e.getMessage(); 
     } 

     try{ 
      this.checkProperty2("someActualResult", "someExpectedResult"); 
     } catch (Exception e){ 
      errMessages += e.getMessage(); 
     } 

     try{ 
      this.checkProperty3("someActualResult", "someExpectedResult"); 
     } catch (Exception e){ 
      errMessages += e.getMessage(); 
     } 

     Assert.assertTrue(errMessages, errMessages.isEmpty()); 


    } 



    private boolean checkProperty1(String propertyValue, String expectedvalue) throws Exception{ 
     if(propertyValue == expectedvalue){ 
      return true; 
     }else { 
      throw new Exception("Property1 has value: " + propertyValue + ", expected: " + expectedvalue); 
     } 
    } 

     private boolean checkProperty2(String propertyValue, String expectedvalue) throws Exception{ 
     if(propertyValue == expectedvalue){ 
      return true; 
     }else { 
      throw new Exception("Property2 has value: " + propertyValue + ", expected: " + expectedvalue); 
     } 
    } 

     private boolean checkProperty3(String propertyValue, String expectedvalue) throws Exception{ 
     if(propertyValue == expectedvalue){ 
      return true; 
     }else { 
      throw new Exception("Property3 has value: " + propertyValue + ", expected: " + expectedvalue); 
     } 
    } 
} 

아마도 가장 좋은 방법은 아니며 과용되면 혼란 스럽지만 ... 가능할 수 있습니다.

+0

나는 모든 용도가 남용이라고 말하고 싶다. 이 모든 것이 테스트 당 단 하나의 단언을 둘러싼 것입니다. – merryprankster

+0

@ merryprankster 글쎄, 만약 당신이 당신을 위해이 작업을 수행 도구가 없다면, 직접 작성하는 것보다. 일단 이것을 많이 사용하면이 일반적인 유틸리티를 만들 수 있습니다. 어떤 시점에서 이것을 테스트 프레임 워크에 추가 할 수도 있습니다. https://github.com/KentBeck/junit/blob/r4.8.2/src/main/java/org/junit/rules/ErrorCollector.java – yoosiba

7

별도로 보관하십시오. 단위 테스트는 어떤 단위가 실패했는지 알려주고 있습니다. 또한 이들을 분리하여 유지하면 긴 디버그 사이클을 거치지 않고도 문제를 신속하게 격리 할 수 ​​있습니다.

+0

부러진 링크 - 제발 수정하고 알려주세요 –

+0

아직 깨진 링크. 그렇기 때문에 링크를 답변의 주요 부분으로 제공하는 것보다 지원을위한 링크를 사용하는 것이 더 좋습니다. – Jay

+0

끊어진 링크가 수정되었습니다. 불편을 드려 죄송합니다. – Nilesh

0

[주의 : 나는 자바/JUnit을 매우 "unfluent", 그래서 아래의 세부 사항에 오류가 조심]

이 할 수있는 몇 가지 방법이있다 :)

1을 여러 주장을 쓰기가 같은 테스트에서. DTO 생성을 한 번만 테스트하는 경우에는이 작업을 수행해야합니다. 이것이 처음 시작할 때 다른 솔루션으로 이동할 수 있습니다.

2) 헬퍼 어설 션을 작성하십시오. assertDtoFieldsEqual, 예상 및 실제 DTO를 전달합니다. 헬퍼 어설 션 내에서는 각 필드를 따로 지정합니다. 이것은 적어도 테스트 당 하나의 주장 만하는 환상을 제공하며 여러 시나리오에 대해 DTO 생성을 테스트하면 더 명확 해집니다.

3) 각 속성을 검사하고 toString을 구현하는 객체에 대해 equals를 구현하여 적어도 어설 션 결과를 수동으로 검사하여 잘못된 부분을 찾습니다.

4) DTO가 생성되는 각 시나리오에 대해 DTO를 생성하고 setUp 메소드에서 예상되는 특성을 초기화하는 별도의 테스트 픽스처를 작성하십시오. 각 속성을 테스트하기위한 별도의 테스트를 만듭니다. 이것은 또한 많은 테스트 결과를 가져 오지만, 최소한 하나의 라이너 일뿐입니다. 의사 코드의 예 : 다양한 시나리오 하에서 DTO 생성을 테스트하고 곧 모든 테스트를 작성하는 지루한 될 수있는 마지막 방법을 선택해야하는 경우

public class WithDtoGeneratedFromXxx : TestFixture 
{ 
    DTO dto = null; 

    public void setUp() 
    { 
    dto = GenerateDtoFromXxx(); 
    expectedProp1 = ""; 
    ... 
    } 

    void testProp1IsGeneratedCorrectly() 
    { 
    assertEqual(expectedProp1, dto.prop1); 
    } 
    ... 
} 

. 이 경우 DTO를 작성하고 예상되는 특성을 파생 클래스에 설정하는 방법에 대한 세부 사항을 생략하는 추상 기본 설비를 구현할 수 있습니다. 의사 코드 :

abstract class AbstractDtoTest : TestFixture 
{ 
    DTO dto; 
    SomeType expectedProp1; 

    abstract DTO createDto(); 
    abstract SomeType getExpectedProp1(); 

    void setUp() 
    { 
    dto = createDto(); 
    ... 
    } 

    void testProp1IsGeneratedCorrectly() 
    { 
    assertEqual(getExpectedProp1(), dto.prop1); 
    } 
    ... 
} 


class WithDtoGeneratedFromXxx : AbstractDtoTest 
{ 
    DTO createDto() { return GenerateDtoFromXxx(); } 
    abstract SomeType getExpectedProp1() { return new SomeType(); } 
    ... 
} 
+0

함수가 모든 속성 필드를 선언하려면 hamcrest 클래스 [SamePropertyValuesAs] (http://hamcrest.org/JavaHamcrest/javadoc/1.3/org/hamcrest/beans/SamePropertyValuesAs.html)를 살펴볼 수 있습니다. . Assert.assertThat (returnedDto, samePropertyValuesAs (expectedDto))에서 사용할 수 있습니다. (더 많은 입력란이 틀린 경우에도 테스트 당 하나의 오류 만 발생합니다.) –

4

only one assert per unit test을 사용 하시겠습니까? 그렇습니다. 그 권고를하는 사람들이 있습니다. 그들이 맞습니까? 나는 그렇게 생각하지 않는다. 저는 그러한 사람들이 실제 코드를 실제로 사용해 왔다고 믿기가 어렵습니다.

그래서 imangine에 유닛 테스트 할 mutator 메소드가 있습니다. 뮤 테이터에는 확인하고 싶은 효과 나 효과가 있습니다. 일반적으로 예상되는 뮤 테이터의 효과는 숫자가 적습니다. 많은 효과가 뮤 테이터에 대해 지나치게 복잡한 디자인을 제시하기 때문입니다. 하나의 assert per assert와 assert 당 하나의 테스트 케이스를 사용하면 mutator 당 많은 테스트 케이스가 필요하지 않으므로 권장 사항이 그렇게 나쁘지는 않습니다.

그러나이 추론의 결함은 이러한 테스트가 뮤 테이터의 예상되는 효과 만보고 있다는 것입니다. 그러나 뮤 테이터에 버그가있는 경우 예기치 않은 잘못된 부작용이있을 수 있습니다. 이 테스트는 코드에 전체 클래스의 버그가 없으며 향후 리팩토링으로 인해 이러한 버그가 발생하지 않는다는 어리석은 가정을합니다. 원래 작성된 메서드는 특정 부작용이 불가능하다는 것을 작성자에게 분명히 알 수 있었지만 리팩터링과 새로운 기능의 추가로 이러한 부작용이 가능할 수있었습니다.

수명이 긴 코드를 테스트하는 유일한 방법은 예상치 못한 부작용이없는 뮤터레이터를 확인하는 것입니다. 그런데 어떻게 테스트 할 수 있습니까? 대부분의 클래스에는 불변의 문자가 있습니다. : 어떤 돌연변이도 바꿀 수없는 것들. 예를 들어 컨테이너의 size 메서드는 절대로 음수 값을 반환하지 않습니다. 각 불변량은 실제로 모든 뮤 테이터 (그리고 생성자)에 대한 사후 조건입니다. 각각의 뮤 테이터에는 일반적으로 변경 될 경우 어떤 종류의 것을 설명하는 불변 조건 집합이 있습니다. 하지 않습니다. sort 메서드는 컨테이너의 길이를 변경하지 않습니다. 클래스 및 뮤 테이터 불변량은 결과적으로 뮤 테이타 호출에 대한 사후 조건입니다. 예상치 못한 부작용을 확인하는 유일한 방법은 이들에 대한 어설 션을 추가하는 것입니다.

더 많은 테스트 케이스를 추가 하시겠습니까? 실제로 invariant의 수에 테스트 할 mutator의 수를 곱한 값이 크기 때문에 테스트 당 하나의 assertion이 많은 테스트 케이스로 이어집니다. 그리고 불변량에 대한 정보는 많은 테스트 케이스에 분산되어 있습니다. 하나의 불변성을 조정하기위한 설계 변경은 많은 테스트 케이스의 변경을 필요로합니다. 그것은 비실용적이됩니다. mutator에 대한 parametrised 테스트 케이스를 가지고있는 것이 더 좋습니다. mutator는 몇 가지 단정을 사용하여 mutator에 대한 여러 불변성을 검사합니다.

관련 문제