복붙노트

[SPRING] Mockito, JUnit 및 Spring

SPRING

Mockito, JUnit 및 Spring

나는 오늘 모키 토에 대해 배우기 시작했다. 나는 간단한 테스트 (JUnit과 함께, 아래 참조)를 작성했지만, Spring의 내부에서 mock 객체를 사용하여 bean을 관리하는 방법을 알 수 없다. Spring과 함께 작업 할 때 가장 좋은 방법은 무엇입니까? 콩에 조롱 한 의존성을 주입하려면 어떻게해야합니까?

내 질문까지 이걸 건너 뛸 수있어.

우선, 내가 배운 것. 이것은 매우 훌륭한 기사입니다. Mocks는 기본을 설명하는 스텁이 아닙니다 (모의 검사 동작 확인은 상태 확인이 아닙니다). 여기 모치 토가 좋은 예가 있습니다. Mockito의 모의 객체가 모의 객체와 스텁이라는 설명이 있습니다.

여기 Mockito와 Matchers에서 더 많은 예제를 찾을 수 있습니다.

이 테스트

@Test
public void testReal(){
    List<String> mockedList = mock(List.class);
     //stubbing
     //when(mockedList.get(0)).thenReturn("first");

    mockedList.get(anyInt());
    OngoingStubbing<String> stub= when(null);
    stub.thenReturn("first");

    //String res = mockedList.get(0);
                //System.out.println(res);

     //you can also verify using argument matcher
     //verify(mockedList).get(anyInt());

    verify(mockedList);
    mockedList.get(anyInt());
}

잘 작동합니다.

내 질문으로 돌아 가라. 여기서 Mockito 모의 객체를 Spring bean에 삽입하면 Spring ReflectionTestUtils.setField ()를 사용하려고 시도하지만, Spring Integration Tests, Mock 객체를 만드는 것보다 Spring의 컨텍스트를 바꿀 것을 권장한다.

나는 지난 두 개의 링크를 정말로 이해하지 못했습니다 ... 누군가 봄이 Mockito와 어떤 문제점을 가지고 있는지 설명 할 수 있습니까? 이 솔루션의 문제점은 무엇입니까?

@InjectMocks
private MyTestObject testObject

@Mock
private MyDependentObject mockedObject

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

https://stackoverflow.com/a/8742745/1137529

편집 : 정말 분명 아니었다. 내 자신을 명확히하는 코드의 3 가지 예를 제공합니다. 우리는 printHello () 메소드를 가진 bean HelloWorld와 HelloWorld의 printHello () 메소드를 호출하는 sayHello 메소드를 가진 bean HelloFacade를 가지고 있다고 가정하자.

첫 번째 예는 의존성 삽입 (DI)을 위해 ReflectionTestUtils를 사용하여 Spring의 컨텍스트와 사용자 정의 러너를 사용하는 것입니다.

public class Hello1Test  {
private ApplicationContext ctx;

@Before
public void setUp() {
    MockitoAnnotations.initMocks(this);
    this.ctx = new ClassPathXmlApplicationContext("META-INF/spring/ServicesImplContext.xml");
}



@Test
public void testHelloFacade() {
    HelloFacade obj = (HelloFacade) ctx.getBean(HelloFacadeImpl.class);
    HelloWorld mock = mock(HelloWorld.class);
    doNothing().when(mock).printHello();

    ReflectionTestUtils.setField(obj, "hello", mock);
    obj.sayHello();

    verify(mock, times(1)).printHello();
}

}

@Noam이 지적했듯이 MockitoAnnotations.initMocks (this));를 명시 적으로 호출하지 않고 실행하는 방법이 있습니다. 이 예제에서는 Spring의 컨텍스트를 사용하여 드롭한다.

@RunWith(MockitoJUnitRunner.class)
public class Hello1aTest {


@InjectMocks
private HelloFacade obj =  new HelloFacadeImpl();

@Mock
private HelloWorld mock;


@Test
public void testHelloFacade() {
    doNothing().when(mock).printHello();
    obj.sayHello();
}

}

이 작업을 수행하는 또 다른 방법

public class Hello1aTest {

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


@InjectMocks
private HelloFacadeImpl obj;

@Mock
private HelloWorld mock;


@Test
public void testHelloFacade() {
    doNothing().when(mock).printHello();
    obj.sayHello();
}

}

Nothing, 예를 들어 수동으로 HelloFacadeImpl을 설치하고 HelloFacade에 할당해야합니다. HelloFacade는 인터페이스입니다. 마지막 예제에서 HelloFacadeImpl을 선언하면 Mokito가이를 인스턴스화합니다. 이 접근 방식의 단점은 현재 테스트중인 유닛이 인터페이스 클래스가 아니며 인터페이스 클래스가 아니라는 것입니다.

해결법

  1. ==============================

    1.솔직히 나는 당신의 질문을 정말로 이해할 수 있을지 확신하지 못합니다 : P 나는 당신이 원문에서 얻은 것부터 가능한 한 더 명확히하려고 노력할 것입니다 :

    솔직히 나는 당신의 질문을 정말로 이해할 수 있을지 확신하지 못합니다 : P 나는 당신이 원문에서 얻은 것부터 가능한 한 더 명확히하려고 노력할 것입니다 :

    첫째로, 대부분의 경우에, 당신은 봄에 어떤 걱정도 있어서는 안됩니다. 단위 테스트를 작성하는 데 봄이 필요하지는 않습니다. 정상적인 경우, 단원 테스트에서 테스트중인 시스템 (SUT, 테스트 할 대상)을 인스턴스화하고 테스트에서 SUT의 종속성도 주입하면됩니다. 의존성은 보통 mock / stub이다.

    원래 제시된 방법으로, 예제 2, 3은 내가 위에서 설명한 것을 정확하게 수행하고 있습니다.

    드문 경우 (예 : 통합 테스트 또는 일부 특수 단위 테스트)에서는 Spring 응용 프로그램 컨텍스트를 만들고 응용 프로그램 컨텍스트에서 SUT를 가져와야합니다. 이런 경우, 저는 다음과 같이 할 수 있다고 생각합니다.

    1) 봄 응용 프로그램 ctx에서 SUT를 만들고 그것에 대한 참조를 얻은 다음 그것에 mock을 주입합니다.

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("test-app-ctx.xml")
    public class FooTest {
    
        @Autowired
        @InjectMocks
        TestTarget sut;
    
        @Mock
        Foo mockFoo;
    
        @Before
        /* Initialized mocks */
        public void setup() {
            MockitoAnnotations.initMocks(this);
        }
    
        @Test
        public void someTest() {
             // ....
        }
    }
    

    또는

    2) Spring Integration Tests, Mock Objects 만들기 링크에 설명 된 방법을 따르십시오. 이 접근법은 Spring의 app 컨텍스트에서 mock을 생성하는 것이며 app ctx에서 mock 객체를 가져 와서 스텁 / 검증을 수행 할 수 있습니다.

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("test-app-ctx.xml")
    public class FooTest {
    
        @Autowired
        TestTarget sut;
    
        @Autowired
        Foo mockFoo;
    
        @Test
        public void someTest() {
             // ....
        }
    }
    

    두 가지 방법 모두 작동해야합니다. 가장 큰 차이점은 이전의 경우에는 스프링의 수명주기 등을 거친 후 (예 : 빈 초기화) 종속성이 주입되는 반면 후자의 경우는 이전에 주입된다는 것입니다. 예를 들어, SUT가 Spring의 InitializingBean을 구현하고 초기화 루틴이 종속성을 포함한다면이 두 접근법의 차이점을 알 수 있습니다. 나는 당신이하고있는 것을 아는 한이 두 가지 접근법에 대해 옳고 그른 것이 없다고 믿습니다.

    보충, @Mock, @Inject, MocktoJunitRunner 등은 모두 모키토 사용에 불필요합니다. 그것들은 Mockito.mock (Foo.class)과 setter 호출을 저장하는 유틸리티입니다.

  2. ==============================

    2.귀하의 질문은 당신이 제시 한 세 가지 사례 중 어느 것이 바람직한 접근인지 묻는 것 같습니다.

    귀하의 질문은 당신이 제시 한 세 가지 사례 중 어느 것이 바람직한 접근인지 묻는 것 같습니다.

    Reflection TestUtils를 사용하는 예제 1은 단위 테스트를위한 좋은 접근 방법이 아닙니다. 당신은 정말로 단위 테스트를 위해 스프링 문맥을 전혀로드하고 싶지 않습니다. 그냥 모의하고 다른 예제에 표시된대로 필요한 것을 투입하십시오.

    일부 통합 테스트를 원한다면 스프링 컨텍스트를로드하고 싶지만 @RunWith (SpringJUnit4ClassRunner.class)를 사용하면 'Bean에 명시 적으로 액세스해야하는 경우 @Autowired와 함께 컨텍스트 로딩을 수행하는 것이 더 좋다.

    예제 2는 유효한 접근법이며 @RunWith (MockitoJUnitRunner.class)를 사용하면 @Before 메서드와 MockitoAnnotations.initMocks (this)에 대한 명시 적 호출을 지정하지 않아도됩니다.

    예제 3은 @RunWith (...)를 사용하지 않는 또 다른 유효한 접근법입니다. 테스트중인 HelloFacadeImpl 클래스를 명시 적으로 인스턴스화하지는 않았지만 예제 2와 동일하게 수행 할 수 있습니다.

    제 제안은 코드 혼란을 줄이기 위해 예제 테스트를 위해 예제 2를 사용하는 것입니다. 강제로 수행해야하는 경우 더 자세한 구성으로 돌아갈 수 있습니다.

  3. ==============================

    3.Spring 4.2.RC1에 새로운 테스트 기능을 추가함으로써 SpringJUnit4ClassRunner에 의존하지 않는 Spring 통합 테스트를 작성할 수 있습니다. 문서의이 부분을 확인하십시오.

    Spring 4.2.RC1에 새로운 테스트 기능을 추가함으로써 SpringJUnit4ClassRunner에 의존하지 않는 Spring 통합 테스트를 작성할 수 있습니다. 문서의이 부분을 확인하십시오.

    귀하의 경우에는 Spring 통합 테스트를 작성하고 다음과 같은 모의 객체를 사용할 수 있습니다.

    @RunWith(MockitoJUnitRunner.class)
    @ContextConfiguration("test-app-ctx.xml")
    public class FooTest {
    
        @ClassRule
        public static final SpringClassRule SPRING_CLASS_RULE = new SpringClassRule();
    
        @Rule
        public final SpringMethodRule springMethodRule = new SpringMethodRule();
    
        @Autowired
        @InjectMocks
        TestTarget sut;
    
        @Mock
        Foo mockFoo;
    
        @Test
        public void someTest() {
             // ....
        }
    }
    
  4. ==============================

    4.MockitoAnnotations.initMocks (this)가 필요하지 않습니다. mockito 1.9 (또는 그 이상)를 사용하고 있다면 - 이것 만 있으면됩니다.

    MockitoAnnotations.initMocks (this)가 필요하지 않습니다. mockito 1.9 (또는 그 이상)를 사용하고 있다면 - 이것 만 있으면됩니다.

    @InjectMocks
    private MyTestObject testObject;
    
    @Mock
    private MyDependentObject mockedObject;
    

    @InjectMocks 주석은 모든 mock을 MyTestObject 객체에 주입합니다.

  5. ==============================

    5.여기에 짧은 요약이 있습니다.

    여기에 짧은 요약이 있습니다.

    단위 테스트를 작성하려면, 클래스 테스트에 참여하는 클래스에 실제 의존성을 삽입하고 싶지 않으므로 Spring applicationContext를 사용하지 마십시오. 대신에 클래스 맨 위에 @RunWith (MockitoJUnitRunner.class) 주석을 사용하거나 @Before 메소드에서 MockitoAnnotations.initMocks (this)를 사용하여 mock을 사용하십시오.

    통합 테스트를 작성하려면 다음을 사용하십시오.

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("yourTestApplicationContext.xml")
    

    예를 들어, 메모리 내 데이터베이스를 사용하여 응용 프로그램 컨텍스트를 설정합니다. 일반적으로 통합 테스트에서는 모의 객체를 사용하지 않지만 앞서 설명한 MockitoAnnotations.initMocks (이) 접근법을 사용하여 수행 할 수 있습니다.

  6. ==============================

    6.@InjectMocks 주석 필드를 인스턴스화해야하는지 여부의 차이는 MockitoJunitRunner 또는 MockitoAnnotations.initMocks를 사용하는지 여부가 아니라 Mockito 버전에 있습니다. 1.9에서는 @Mock 필드의 생성자 삽입도 처리 할 것이므로 인스턴스 생성을 수행 할 것입니다. 이전 버전에서는 직접 인스턴스를 생성해야했습니다.

    @InjectMocks 주석 필드를 인스턴스화해야하는지 여부의 차이는 MockitoJunitRunner 또는 MockitoAnnotations.initMocks를 사용하는지 여부가 아니라 Mockito 버전에 있습니다. 1.9에서는 @Mock 필드의 생성자 삽입도 처리 할 것이므로 인스턴스 생성을 수행 할 것입니다. 이전 버전에서는 직접 인스턴스를 생성해야했습니다.

    이것이 내가 Spring Bean의 단위 테스트를 수행하는 방법이다. 문제 없습니다. 사람들은 혼란에 빠지며 Spring 구성 파일을 사용하여 실제로 단위 테스트 및 통합 테스트의 요점을 넘어서는 모의 주입을 수행하려고합니다.

    물론 테스트중인 장치는 Impl입니다. 진짜 구체적인 것을 시험해 봐야 해요, 그렇죠? 인터페이스로 선언 한 경우에도 테스트하기 위해 실제 인스턴스를 인스턴스화해야합니다. 자, 당신은 스파이에 빠질 수 있습니다. 스파이는 실물을 감싸는 스텁 / 모의 래퍼입니다.

  7. ==============================

    7.프로젝트를 Spring Boot 1.4로 마이그레이션한다면 새로운 주석 @MockBean을 사용하여 MyDependentObject를 위조 할 수 있습니다. 이 기능을 사용하면 테스트에서 Mockito의 @Mock 및 @InjectMocks 주석을 제거 할 수 있습니다.

    프로젝트를 Spring Boot 1.4로 마이그레이션한다면 새로운 주석 @MockBean을 사용하여 MyDependentObject를 위조 할 수 있습니다. 이 기능을 사용하면 테스트에서 Mockito의 @Mock 및 @InjectMocks 주석을 제거 할 수 있습니다.

  8. from https://stackoverflow.com/questions/10906945/mockito-junit-and-spring by cc-by-sa and MIT license