[java] 모의 객체 초기화-MockIto

MockIto를 사용하여 모의 객체를 초기화하는 방법에는 여러 가지가 있습니다. 이들 중 가장 좋은 방법은 무엇입니까?

1.

 public class SampleBaseTestCase {

   @Before public void initMocks() {
       MockitoAnnotations.initMocks(this);
   }

2.

@RunWith(MockitoJUnitRunner.class)

[편집] 3.

mock(XXX.class);

이것보다 더 나은 방법이 있다면 나에게 제안하십시오 …



답변

mocks 초기화의 경우 runner 또는를 사용하는 것은 완전히 MockitoAnnotations.initMocks동등한 솔루션입니다. MockitoJUnitRunner 의 javadoc에서 :

JUnit 4.5 runner initializes mocks annotated with Mock, so that explicit usage of MockitoAnnotations.initMocks(Object) is not necessary. Mocks are initialized before each test method.


첫 번째 솔루션 (사용 MockitoAnnotations.initMocks)은 이미 특정 러너 (SpringJUnit4ClassRunner )은 테스트 케이스에서 예 .

두 번째 솔루션 ( MockitoJUnitRunner)은 더 클래식하고 제가 가장 좋아하는 솔루션 입니다. 코드는 더 간단합니다. 러너를 사용하면 프레임 워크 사용에 대한 자동 유효성 검사의 큰 이점이 있습니다 ( 이 답변 에서 @David Wallace 설명 ).

두 솔루션 모두 테스트 방법간에 모의 (및 ​​스파이)를 공유 할 수 있습니다. 와 함께 사용하면 @InjectMocks단위 테스트를 매우 빠르게 작성할 수 있습니다. 상용구 모의 코드가 줄어들고 테스트가 더 읽기 쉽습니다. 예를 들면 :

@RunWith(MockitoJUnitRunner.class)
public class ArticleManagerTest {

    @Mock private ArticleCalculator calculator;
    @Mock(name = "database") private ArticleDatabase dbMock;
    @Spy private UserProvider userProvider = new ConsumerUserProvider();

    @InjectMocks private ArticleManager manager;

    @Test public void shouldDoSomething() {
        manager.initiateArticle();
        verify(database).addListener(any(ArticleListener.class));
    }

    @Test public void shouldDoSomethingElse() {
        manager.finishArticle();
        verify(database).removeListener(any(ArticleListener.class));
    }
}

장점 : 코드가 최소화 됨

단점 : 흑 마법. IMO는 주로 @InjectMocks 주석 때문입니다. 이 주석을 사용하면 “코드의 고통있습니다.” ( @ Brice 의 훌륭한 댓글 참조 )


세 번째 해결책은 각 테스트 방법에 대한 모의를 만드는 것입니다. @mlk 에 의해 설명 된 대로자체 테스트 ” 를 가질 수 있습니다 .

public class ArticleManagerTest {

    @Test public void shouldDoSomething() {
        // given
        ArticleCalculator calculator = mock(ArticleCalculator.class);
        ArticleDatabase database = mock(ArticleDatabase.class);
        UserProvider userProvider = spy(new ConsumerUserProvider());
        ArticleManager manager = new ArticleManager(calculator,
                                                    userProvider,
                                                    database);

        // when 
        manager.initiateArticle();

        // then 
        verify(database).addListener(any(ArticleListener.class));
    }

    @Test public void shouldDoSomethingElse() {
        // given
        ArticleCalculator calculator = mock(ArticleCalculator.class);
        ArticleDatabase database = mock(ArticleDatabase.class);
        UserProvider userProvider = spy(new ConsumerUserProvider());
        ArticleManager manager = new ArticleManager(calculator,
                                                    userProvider,
                                                    database);

        // when 
        manager.finishArticle();

        // then
        verify(database).removeListener(any(ArticleListener.class));
    }
}

장점 : API 작동 방식을 명확하게 보여줍니다 (BDD …)

단점 : 더 많은 상용구 코드가 있습니다. (모의 생성)


나의 추천 은 타협입니다. @Mock와 함께 주석을 사용 @RunWith(MockitoJUnitRunner.class)하되는 사용하지 마십시오 @InjectMocks.

@RunWith(MockitoJUnitRunner.class)
public class ArticleManagerTest {

    @Mock private ArticleCalculator calculator;
    @Mock private ArticleDatabase database;
    @Spy private UserProvider userProvider = new ConsumerUserProvider();

    @Test public void shouldDoSomething() {
        // given
        ArticleManager manager = new ArticleManager(calculator,
                                                    userProvider,
                                                    database);

        // when 
        manager.initiateArticle();

        // then 
        verify(database).addListener(any(ArticleListener.class));
    }

    @Test public void shouldDoSomethingElse() {
        // given
        ArticleManager manager = new ArticleManager(calculator,
                                                    userProvider,
                                                    database);

        // when 
        manager.finishArticle();

        // then 
        verify(database).removeListener(any(ArticleListener.class));
    }
}

장점 : API 작동 방식을 명확하게 보여줍니다 (내가 ArticleManager인스턴스화되는 방식). 상용구 코드가 없습니다.

단점 : 테스트는 자체 포함되지 않으며 코드의 고통이 적습니다.


답변

이제 (v1.10.7부터) MockitoRule 이라는 JUnit4 규칙을 사용하는 모의를 인스턴스화하는 네 번째 방법이 있습니다.

@RunWith(JUnit4.class)   // or a different runner of your choice
public class YourTest
  @Rule public MockitoRule rule = MockitoJUnit.rule();
  @Mock public YourMock yourMock;

  @Test public void yourTestMethod() { /* ... */ }
}

JUnit은 @Rule로 주석이 달린 TestRule의 하위 클래스를 찾고 이를 사용 하여 Runner가 제공하는 테스트 문래핑합니다 . 이것의 결론은 @Before 메소드, @After 메소드를 추출 할 수 있으며 심지어 try … catch 래퍼를 규칙에 넣을 수 있다는 것입니다. ExpectedException 이 수행 하는 방식으로 테스트 내에서 이들과 상호 작용할 수도 있습니다 .

MockitoRule은 Parameterized 와 같은 다른 실행기를 사용할 수 있다는 점을 제외하면 MockitoJUnitRunner와 거의 동일하게 작동합니다. (테스트 생성자가 인수를 받아 테스트를 여러 번 실행할 수 있도록 허용) 또는 Robolectric의 테스트 실행기 합니다. Android 네이티브 클래스의 경우). 따라서 최신 JUnit 및 Mockito 버전에서 사용하는 것이 훨씬 더 유연 해집니다.

요약해서 말하자면:

  • Mockito.mock(): 어노테이션 지원 또는 사용 유효성 검증없이 직접 호출.
  • MockitoAnnotations.initMocks(this): 주석 지원, 사용 확인 없음.
  • MockitoJUnitRunner: 주석 지원 및 사용 유효성 검사,하지만 해당 러너를 사용해야합니다.
  • MockitoRule: 모든 JUnit 실행기를 사용한 주석 지원 및 사용 유효성 검증.

참조 : JUnit @Rule의 작동 원리


답변

이를 수행하는 깔끔한 방법이 있습니다.

  • 단위 테스트 인 경우 다음을 수행 할 수 있습니다.

    @RunWith(MockitoJUnitRunner.class)
    public class MyUnitTest {
    
        @Mock
        private MyFirstMock myFirstMock;
    
        @Mock
        private MySecondMock mySecondMock;
    
        @Spy
        private MySpiedClass mySpiedClass = new MySpiedClass();
    
        // It's gonna inject the 2 mocks and the spied object per reflection to this object
        // The java doc of @InjectMocks explains it really well how and when it does the injection
        @InjectMocks
        private MyClassToTest myClassToTest;
    
        @Test
        public void testSomething() {
        }
    }
  • 편집 : 통합 테스트 인 경우 다음을 수행 할 수 있습니다 (Spring에서는 그런 방식으로 사용할 수 없습니다. 다른 러너로 모의를 초기화 할 수 있음을 보여주세요) :

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("aplicationContext.xml")
    public class MyIntegrationTest {
    
        @Mock
        private MyFirstMock myFirstMock;
    
        @Mock
        private MySecondMock mySecondMock;
    
        @Spy
        private MySpiedClass mySpiedClass = new MySpiedClass();
    
        // It's gonna inject the 2 mocks and the spied object per reflection to this object
        // The java doc of @InjectMocks explains it really well how and when it does the injection
        @InjectMocks
        private MyClassToTest myClassToTest;
    
        @Before
        public void setUp() throws Exception {
              MockitoAnnotations.initMocks(this);
        }
    
        @Test
        public void testSomething() {
        }
    }

답변

MockitoAnnotations 및 주자는 위에서 잘 논의되었으므로 사랑받지 못하는 사람들을 위해 제 조롱을 던질 것입니다.

XXX mockedXxx = mock(XXX.class);

나는 그것이 조금 더 설명 적이기 때문에 이것을 사용하고, 내 테스트가 (가능한 한) 자체 포함되는 것을 좋아하기 때문에 멤버 변수를 사용하지 않는 (올바른 금지가 아닌) 단위 테스트를 선호하기 때문에 사용합니다.


답변

JUnit 5 Jupiter의 간단한 예인 “RunWith”가 제거되었으므로 이제 “@ExtendWith”주석을 사용하여 확장 기능을 사용해야합니다.

@ExtendWith(MockitoExtension.class)
class FooTest {

  @InjectMocks
  ClassUnderTest test = new ClassUnderTest();

  @Spy
  SomeInject bla = new SomeInject();
}


답변

다른 답변은 훌륭하며 원하거나 필요한 경우 더 자세한 정보를 포함합니다.
그 외에도 TL; DR을 추가하고 싶습니다.

  1. 사용 선호
    • @RunWith(MockitoJUnitRunner.class)
  2. 이미 다른 러너를 사용하고 있기 때문에 할 수없는 경우
    • @Rule public MockitoRule rule = MockitoJUnit.rule();
  3. (2)하지만, 당신이해야 할 유사 하지 더 이상이 사용
    • @Before public void initMocks() {
      MockitoAnnotations.initMocks(this);
      }
  4. 테스트 중 하나에서만 mock을 사용하고 동일한 테스트 클래스의 다른 테스트에 노출하지 않으려면 다음을 사용하십시오.
    • X x = mock(X.class)

(1) 및 (2) 및 (3)은 상호 배타적입니다.
(4) 다른 것과 함께 사용할 수 있습니다.


답변