• 참고 URL
    • https://code.google.com/p/powermock/wiki/MockitoUsage13
    • http://stackoverflow.com/questions/11458963/mockito-0-matchers-expected-1-recorded-invaliduseofmatchersexception

단위테스트를 작성하다보면 환경에 따라 다르게 동작하거나 static method 로 인해 특정 케이스를 재현하기 어려운 경우가 존재한다.
단위테스트는 환경에 영향받지 않고 어떤 환경이든 해당 단위(메소드 또는 기능)에 대해 의도한 대로 로직이 구현되어있음을 확인하는것으로
이러한 경우에 Mock 객체를 활용하여 단위테스트를 구성할 수 있다.

일반적인 object의 method에 대해서는 왠만한 사람들이라면 Easymock, Mockito, Powermock 등 다양한 라이브러리를 통해 테스트케이스를 생성한다.
하지만 static method나 private method에 대해서는 일반적인 mock 생성을 통해 조작이 불가하며 reflection을 통해 객체를 가로채어 바꾸어주어야 한다.

예전에는 Whitebox 라는 메소드인지 클래스인지..를 통해서 private method에 대한 mocking을 설정했던것 같은데..
여기서는 Powermock을 통해 static method를 mock 처리하는 방법에 대해 정리하고자 한다.
+) 테스트를 작성하다 보니 transactionmanager.getTransaction()을 mocking 하는데 matcher도 규격대로 작성했건만 에러가 발생한다. 이 부분에 대해 추가정리!

STATIC METHOD Mocking

1.  library 준비

  • http://mvnrepository.com/artifact/org.powermock
    • powermock-module-junit : junit과 연동하기위한 dependency. Runner class가 들어있다.
    • powermock-api-mockito : Mock 처리를 위한 dependency. Powermockito class가 들어있다.
2. Test Case 에 적용.


@RunWith(PowerMockRunner.class)
@PrepareForTest(Static.class) // mock 처리하고싶은 static method를 보유한 class 
public class StaticMockingTest {
    private AAA mockAAA;

    @Test
    public void test() {
        // GIVEN
        // 일반적인 Mock 객체 생성 및 mock 처리
        this.mockAAA = PowerMockito.mock(AAA.class);
        PowerMockito.when(mockAAA.getValue("key")).thenReturn("value"); // case에 따라 matcher를 사용하여 범용처리를 하기도 한다.

        // static method Mock 처리
        PowerMockito.mockStatic(Static.class);
        
PowerMockito.when(Static.getInstance()).thenReturn(mockAAA); // static method가 호출되었을때에 대한 mock 처리
        // static mock 처리에는 Class 위에 선언된  @RunWith, @PrepareForTest annotation이 설정되어있어야만 정상 동작한다.

        // WHEN
       
Assert.assertEquals(mockStatic, Static.getInstance());

        // THEN
        PowerMockito.verifyStatic();
    }
}



FINAL METHOD Mocking

  • 참고 URL
    • http://stackoverflow.com/questions/11458963/mockito-0-matchers-expected-1-recorded-invaliduseofmatchersexception
    • http://docs.spring.io/spring-framework/docs/2.5.6/api/org/springframework/transaction/support/AbstractPlatformTransactionManager.html#getTransaction(org.springframework.transaction.TransactionDefinition)
    • http://docs.spring.io/spring-framework/docs/2.0.x/api/org/springframework/transaction/PlatformTransactionManager.html#getTransaction(org.springframework.transaction.TransactionDefinition)

 [Logic] - 메소드 내에 다음과 같은 로직이 들어있었다. 바꾸기는 부담스럽고(history를 알 수 없어서), 일단 테스트를 하려고보니...

 @Autowired // 사실은 spring version이 낮아 setter method 로 들어갔음.
 DataSourceTransactionManager transactionManager; 

 DefaultTransactionDefinition def = new DefaultTransactionDefinition();
 def.setName("transaction");
 def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
 TransactionStatus status = transactionManager.getTransaction(def);

 [TEST] - transactionManager.getTransaction(def) 이 statement를 mocking 하기위해 다음과 같이 했더니 에러가 발생한다. ??

 when(mockTransactionManager.getTransaction(any(DefaultTransactionDefinition.class))).thenReturn(mockTransactionStatus);

 org.mockito.exceptions.misusing.InvalidUseOfMatchersException: 
      
Invalid use of argument matchers!
      
0 matchers expected, 1 recorded:
 
This exception may occur if matchers are combined with raw values:
      
//incorrect:
      
someMethod(anyObject(), "raw String");
 
When using matchers, all arguments have to be provided by matchers.
      
For example:
      //correct:
      
someMethod(anyObject(), eq("String by matcher"));      

 뭐가 문제인지 몰라 한참을 확인해보니.... 구현체의 메소드가 final로 선언되어 변경이 불가능한것이었다! 그래서..저런에러가..

  • transactionManager의 구현체인 DataSourceTransactionManager의 getTransaction 메소드는 final로 선언되어 mocking 불가
    (DataSourceTransactionManager가 상속한 AbstractPlatformTransactionManager 부터 getTransaction 메서드는 final로 선언되어있으며, final method를 mocking하기 위해서는 static method와 마찬가지로 설정해주어야 한다.)
 그래서인지 실제 로직에서는 인터페이스를 통해 구현체를 주입받도록 권고하나 보다..
 이 TransactionManager의 interface인 PlatformTransactionManager는 getTransaction 메서드가 public으로 선언되어있어 일반적인 방법으로도 충분히 mocking이 가능하다.











'myplace' 카테고리의 다른 글

Maven test와 junit의 동작 차이 (@Ignore annotation이 다르게 동작해요)  (0) 2015.05.14
SonarQube  (0) 2014.09.06
jenkins CI Server  (0) 2014.09.05
Posted by dreamhopp
,
  • 참고 URL :
    • http://junit.10954.n7.nabble.com/maven2-with-Junit-4-ignores-Test-annotations-td8629.html
    • http://maven.apache.org/surefire/maven-surefire-plugin/examples/junit.html
    • http://stackoverflow.com/questions/7535177/if-i-ignore-a-test-class-in-junit4-does-beforeclass-still-run

IDE에서 Junit4로 테스트시 @Ignore 등 annotation 정상동작함.
mvn clean compile test 명령어를 통해 Maven으로 test 수행시 @Ignore 등 annotation이 무시되어 Ignore처리된 테스트도 수행됨(결과는 failure로 잡힘)

원인은 못찾음. 뭔가 동일한 테스트 클래스가 존재한다는 말은 있지만..

해결방법은 @Ignore 처리한 Class를 maven 설정에서 exclude에 추가.

<build>설정에 다음내용 추가

<plugin>

                <groupId>org.apache.maven.plugins</groupId>

                <artifactId>maven-surefire-plugin</artifactId>

                <version>2.8</version>

                <configuration>

                    <includes>

                        <include>**/*Test.java</include>

                    </includes>

                    <excludes>

                        <exclude>**/*DAOTest.java</exclude>

                    </excludes>

                </configuration>

                <dependencies>

                    <dependency>

                        <groupId>org.apache.maven.surefire</groupId>

                        <!-- Use the older JUnit 4 provider -->

                        <artifactId>surefire-junit4</artifactId>

                        <version>2.8</version>

                    </dependency>

                </dependencies>

            </plugin> 


'myplace' 카테고리의 다른 글

Static, Final Method Unit Test  (0) 2015.05.14
SonarQube  (0) 2014.09.06
jenkins CI Server  (0) 2014.09.05
Posted by dreamhopp
,