2014-04-16 2 views
1

단위 테스트를하고 있습니다. 우리 프로젝트는 Play! 뼈대. 단위 테스트는 Java로 작성됩니다. 우리 팀이 @RunWith (PowerMockRunner.class)로 주석을 달고 다른 클래스를 확장하는 테스트 클래스에 대해 중복 테스트를 경험 한 문제가 발생했습니다.@RunWith (PowerMockRunner.class) 주석이있는 테스트 클래스는 다른 클래스를 확장 할 때 테스트 케이스를 두 번 실행합니다.

단위 테스트 하네스 : 다음과 같이 테스트 클래스에 대한

우리의 주요 셋업은 다양한 테스트 케이스 공유 기능이 포함되어 있습니다. 또한 일반적으로 사용되는 필드를 포함합니다.

@Ignore 
@RunWith(PowerMockRunner.class) 
@PrepareForTest(SomeStaticClass.class) 
public abstract class UnitTestHarness { 
    //Bunch of setup code... 
} 

테스트 클래스

: 우리가 테스트하고있는 클래스에 대한 테스트 케이스를 포함합니다.

public class TestClass extends UnitTestHarness { 
    @Test 
    public void testSomething(){ 
     //Perform some tests... 
    } 
} 

당신이 볼 수 있듯이, TestClass에이 주석을 가지고 UnitTestHarness를 확장합니다. TestClass에이 놀이로 실행하면 "테스트 전용"명령은 다음과 같은 결과가 반환됩니다

[info] somepackage.TestClass 
[info] + testSomething 
[info] 
[info] 
[info] Total for test somepackage.TestClass 
[info] Finished in 0.055 seconds 
[info] 1 tests, 0 failures, 0 errors 
[info] somepackage.TestClass 
[info] + testSomething 
[info] 
[info] 
[info] Total for test somepackage.TestClass 
[info] Finished in 0.001 seconds 
[info] 4 tests, 0 failures, 0 errors 
[info] Passed: Total 4, Failed 0, Errors 0, Passed 4 
[success] Total time: 18 s, completed Apr 16, 2014 4:19:37 PM 

는 물론, 시험은 어떤 이유로 두 번 실행되고있다. @RunWith 주석과 확장 클래스에 문제가 있다는 것을 온라인에서 읽었지만 해결책을 찾지 못했습니다.

이 경우
[info] somepackage.TestClass 
[info] 
[info] 
[info] Total for test somepackage.TestClass 
[info] Finished in 0.032 seconds 
[info] 0 tests, 0 failures, 0 errors 
[info] somepackage.TestClass 
[info] + testSomething 
[info] 
[info] 
[info] Total for test somepackage.TestClass 
[info] Finished in 0.025 seconds 
[info] 4 tests, 0 failures, 0 errors 
[info] Passed: Total 4, Failed 0, Errors 0, Passed 4 
[success] Total time: 13 s, completed Apr 16, 2014 4:47:28 PM 

, 우리는 여전히 테스트의 일종을 실행하려고 것을 볼 수 있습니다 : 나는 또한 내가의 TestClass에 UnitTestHarness에서 주석을 이동하는 경우, 다음과 같은 출력이 반환됩니다 참고하기 가져 느낄 UnitTestHarness에 주석이 없으므로 실제로는 추가로 4를 실행하지 않습니다. 첫 번째 예에서는 UnitTestHarness에 주석을 추가하고 TestClass에 상속되었지만이 예제에서는 TestClass에만 주석이 있으므로이 특정 클래스에 대해서만 테스트가 실행되기 때문입니다.

누구든지이 문제를 보았습니까? 분명히 우리는 두 번째 시나리오로 인해 만들 수 있지만, 그 클래스를 실행하려는 추가 시도가 이상적으로 일어나지 않아야합니다. PowerMockRunner 또는 Play 프레임 워크에 문제가 있습니까? 테스트는 전체 테스트 스위트를 실행하기 위해 mvn 새로 설치 또는 재생 테스트를 실행할 때 두 번 실행됩니다. 가능한 해결책이나 제안 사항은 이며 크게입니다. 도와 주셔서 대단히 감사합니다.

제안 된 솔루션 # 1 : UnitTestHarness를 주입

내가 MockitoAnnotations.initMocks (이)를 사용하여 두 번 실행하지를 얻을 수있었습니다; 언급 한 특수 효과 대신 어노테이션을 사용할 때 테스트는 여전히 두 번 실행됩니다. 필자는 설명 된이 방법을 사용하려했지만 주석이 있거나 없을 때 다양한 문제가 발생했습니다.

1) @InjectMocks를 올바르게 사용하는 것처럼 보이지 않습니다. 나는 그 주석을 사용했으며, 실제로 그 유형의 필드를 가진 다른 객체에 주입하고자하는 필드를 조롱했을 때 사용하도록되어 있습니다.

2) UnitTestHarness의 모의 동작이 이러한 방식으로 주입 될 때 전송되지 않는다는 것을 발견했습니다. 유닛 테스트 하네스는 현재 Http.Context를 설정하고 테스트 케이스로 전송하지 않습니다.사실, UnitTestHarness를 유용하게 만든 많은 초기화 작업은 TestClass 자체의 @Before 메서드로 옮겨 져야만합니다.이 메서드는 처음에 하네스를 사용하는 목적을 상실합니다.

3) 하네스에서 사용하고있는 모든 방법은 접근성을 위해 공개되어야하며 TestClass 자체의 하네스 또는 정적 호출을 통해 액세스해야합니다. 하네스의 필드를 공개하거나 대다수의 뮤 테이션을 만들 필요가 있습니다.

전체적으로 나는 이것이 우리가 가진 문제에 대한 좋은 해결책이라고 생각하지 않지만, 귀하의 생각에 감사드립니다.

+0

기본 클래스를 추상화하십시오. –

+0

미안하지만, 나는 그것이 추상적이라는 것을 보여 주려고 간과했다. 나는 내 질문을 편집 할 것이다. – sofarabbit

답변

1

@InjectMocks 주석을 사용하여 TestClass에 UnitTestHarness를 주입하고 @Before로 주석 된 메소드에서 필요한 모든 메소드를 호출하려고 했습니까? @InjectMocks를 사용할 때 @RunWith (MockitoJUnitRunner.class)라는 클래스 이름을 추가해야한다는 것을 기억하십시오. Here은 몇 가지 예입니다.

상속을 피하는 것이 항상 좋은 선택입니다.

편집 :

이 문제가 라이브러리에없는 것을 나에게 보인다. 나는 몇 가지 코드를 작성하고 그것을 잘 작동입니다 :

Running service.TestClass 
Array element:firstElement 
Array element:secondElement 
Array element:thirdElement 
First test method. Value of field1 from UnitTestHarness is: field1 
First test method. Value of field1 from UnitTestHarness is: New value 
Second test method. Value of field2 from UnitTestHarness is: field2 
This static method is mocked! 
My point: 2.0, 3.0 
Tests run: 5, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.442 sec 

Results : 

Tests run: 5, Failures: 0, Errors: 0, Skipped: 0 

는 어쩌면 당신이 더 많은 코드를 추가해야 뭔가 더 구체적인 조언을 어려운이 문제의 넓은 맥락없이이 내 콘솔 출력

public final class ClassWithStaticMethods { 

    public static String returnString(){ 
     return "Sample text"; 
    } 
} 

@RunWith(PowerMockRunner.class) 
@PrepareForTest(ClassWithStaticMethods.class) 
public class UnitTestHarness { 

    private String field1 = "field1"; 
    private String field2 = "field2"; 

    @Before 
    public void setUp() { 
     PowerMockito.mockStatic(ClassWithStaticMethods.class); 
     when(ClassWithStaticMethods.returnString()).thenReturn("This static method is mocked!"); 
    } 

    public List<String> createList() { 
     List<String> list = Lists.newArrayList(); 
     list.add("firstElement"); 
     list.add("secondElement"); 
     list.add("thirdElement"); 

     return list; 
    } 

    public Point dummyPoint() { 
     return new Point(2, 3); 
    } 

    public String getField1() { 
     return field1; 
    } 

    public void setField1(String field1) { 
     this.field1 = field1; 
    } 

    public String getField2() { 
     return field2; 
    } 

    public void setField2(String field2) { 
     this.field2 = field2; 
    } 
} 


public class TestClass extends UnitTestHarness { 

    private List<String> list; 
    private Point dummyPoint; 

    @Before 
    public void setup() { 
     list = createList(); 
     dummyPoint = dummyPoint(); 
    } 

    @Test 
    public void testSomething() { 
     System.out.println("First test method. Value of field1 from UnitTestHarness is: " + getField1()); 
     setField1("New value"); 
     System.out.println("First test method. Value of field1 from UnitTestHarness is: " + getField1()); 
    } 

    @Test 
    public void testSomethingElse() { 
     System.out.println("Second test method. Value of field2 from UnitTestHarness is: " + getField2()); 
    } 

    @Test 
    public void testStaticMockMethod() { 
     System.out.println(ClassWithStaticMethods.returnString()); 
    } 

    @Test 
    public void testDummyPoint() { 
     System.out.println(String.format("My point: %s, %s", dummyPoint.getX(), dummyPoint.getY())); 
    } 

    @Test 
    public void testArray() { 
     for (String string : list) { 
      System.out.println("Array element:" + string); 
     } 
    } 
} 

이 수업 안에 있습니까?

+0

편집 : 귀하의 편집을 기반으로, 나는 정말 이상한 찾으십시오. 어노테이션이 확장 된 결과로 두 번 테스트를 실행하는 것이 궁금합니다 ... JUnit과 조롱 의존성에 대한 모든 버전을 확인했으며 최신 상태 인 것처럼 보였습니다. 당신의 예에서의 셋업은 우리가 가진 것과 매우 흡사합니다. 모든 정적 및 비 정적 mock은 @Before 메소드에서 선언됩니다. 그 외에도 메소드에서 일관되게 반환되는 특정 객체 (즉, 컨트롤러 클래스의 결과)를 확인하는 몇 가지 추가 보호 메소드가 있습니다. 곧 발견 한 솔루션을 게시 할 예정입니다. – sofarabbit

관련 문제