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을 추가하고 싶습니다.
- 사용 선호
@RunWith(MockitoJUnitRunner.class)
- 이미 다른 러너를 사용하고 있기 때문에 할 수없는 경우
@Rule public MockitoRule rule = MockitoJUnit.rule();
- (2)하지만, 당신이해야 할 유사 하지 더 이상이 사용
@Before public void initMocks() {
MockitoAnnotations.initMocks(this);
}
- 테스트 중 하나에서만 mock을 사용하고 동일한 테스트 클래스의 다른 테스트에 노출하지 않으려면 다음을 사용하십시오.
X x = mock(X.class)
(1) 및 (2) 및 (3)은 상호 배타적입니다.
(4) 다른 것과 함께 사용할 수 있습니다.