[spring] 봄 콩에 Mockito 모의 주입

JUnit을 사용한 단위 테스트를 위해 Mockito 모의 객체를 Spring (3+) Bean에 주입하고 싶습니다. 내 Bean 종속성은 현재 @Autowired개인 멤버 필드 의 주석을 사용하여 주입됩니다 .

사용을 고려 ReflectionTestUtils.setField했지만 주입하려는 Bean 인스턴스는 실제로 프록시이므로 대상 클래스의 개인 멤버 필드를 선언하지 않습니다. 테스트 목적으로 인터페이스를 순수하게 수정하므로 종속성에 대한 공용 세터를 만들고 싶지 않습니다.

Spring 커뮤니티에서 제공 한 조언 을 따랐 지만 모의가 생성되지 않고 자동 배선이 실패합니다.

<bean id="dao" class="org.mockito.Mockito" factory-method="mock">
    <constructor-arg value="com.package.Dao" />
</bean>

현재 발생하는 오류는 다음과 같습니다.

...
Caused by: org...NoSuchBeanDefinitionException:
    No matching bean of type [com.package.Dao] found for dependency:
    expected at least 1 bean which qualifies as autowire candidate for this dependency.
    Dependency annotations: {
        @org...Autowired(required=true),
        @org...Qualifier(value=dao)
    }
at org...DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(D...y.java:901)
at org...DefaultListableBeanFactory.doResolveDependency(D...y.java:770)

constructor-arg값을 잘못된 것으로 설정 하면 응용 프로그램 컨텍스트를 시작할 때 오류가 발생하지 않습니다.



답변

가장 좋은 방법은 다음과 같습니다.

<bean id="dao" class="org.mockito.Mockito" factory-method="mock">
    <constructor-arg value="com.package.Dao" />
</bean> 

업데이트
컨텍스트 파일에서이 모형은 선언 된대로 자동 유선 필드보다 먼저 나열되어야합니다.


답변

@InjectMocks
private MyTestObject testObject;

@Mock
private MyDependentObject mockedObject;

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

이것은 조롱 된 객체를 테스트 클래스에 주입합니다. 이 경우 mockedObject를 testObject에 주입합니다. 이것은 위에서 언급되었지만 여기에 코드가 있습니다.


답변

Spring Java Config 및 Mockito를 사용하는 매우 간단한 솔루션이 있습니다.

@Configuration
public class TestConfig {

    @Mock BeanA beanA;
    @Mock BeanB beanB;

    public TestConfig() {
        MockitoAnnotations.initMocks(this); //This is a key
    }

    //You basically generate getters and add @Bean annotation everywhere
    @Bean
    public BeanA getBeanA() {
        return beanA;
    }

    @Bean
    public BeanB getBeanB() {
        return beanB;
    }
}


답변

주어진:

@Service
public class MyService {
    @Autowired
    private MyDAO myDAO;

    // etc
}

자동 배선을 통해 테스트중인 클래스를로드하고 Mockito와의 종속성을 모의 한 다음 Spring의 ReflectionTestUtils를 사용하여 테스트중인 클래스에 모의 객체를 주입 할 수 있습니다.

@ContextConfiguration(classes = { MvcConfiguration.class })
@RunWith(SpringJUnit4ClassRunner.class)
public class MyServiceTest {
    @Autowired
    private MyService myService;

    private MyDAO myDAOMock;

    @Before
    public void before() {
        myDAOMock = Mockito.mock(MyDAO.class);
        ReflectionTestUtils.setField(myService, "myDAO", myDAOMock);
    }

    // etc
}

Spring 4.3.1 이전에는이 ​​메소드가 프록시 뒤의 서비스 ( 예 : @Transactional, 또는 Cacheable)로 작동하지 않습니다 . 이것은 SPR-14050에 의해 수정되었습니다 .

이전 버전의 경우 해결책은 다음과 같이 프록시 래핑을 해제하는 것입니다. 트랜잭션 주석은 서비스가 조롱되는 것을 방지합니다 ( ReflectionTestUtils.setField현재 기본적으로 수행되는 작업).


답변

Spring Boot 1.4를 사용하는 경우이 작업을 수행하는 멋진 방법이 있습니다. @SpringBootTest수업과 @MockBean현장 에서 새로운 브랜드 를 사용하기 만하면 Spring Boot는이 유형의 모형을 만들어 문맥에 주입합니다 (원래 브랜드 를 주입하는 대신).

@RunWith(SpringRunner.class)
@SpringBootTest
public class MyTests {

    @MockBean
    private RemoteService remoteService;

    @Autowired
    private Reverser reverser;

    @Test
    public void exampleTest() {
        // RemoteService has been injected into the reverser bean
        given(this.remoteService.someCall()).willReturn("mock");
        String reverse = reverser.reverseSomeCall();
        assertThat(reverse).isEqualTo("kcom");
    }

}

반면에 Spring Boot를 사용하지 않거나 이전 버전을 사용하는 경우 약간 더 많은 작업을 수행해야합니다.

@ConfigurationSpring 컨텍스트에 모의 객체를 주입 하는 Bean을 작성하십시오 .

@Configuration
@Profile("useMocks")
public class MockConfigurer {

    @Bean
    @Primary
    public MyBean myBeanSpy() {
        return mock(MyBean.class);
    }
}

@Primary주석을 사용하면 한정자가 지정되지 않은 경우이 bean이 우선 순위를 갖도록 스프링에 알립니다.

@Profile("useMocks")모의를 사용할 클래스와 실제 Bean을 사용할 클래스를 제어 하려면 클래스에 주석을 달아야합니다 .

마지막으로 테스트에서 userMocks프로파일을 활성화하십시오 .

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = {Application.class})
@WebIntegrationTest
@ActiveProfiles(profiles={"useMocks"})
public class YourIntegrationTestIT {

    @Inject
    private MyBean myBean; //It will be the mock!


    @Test
    public void test() {
        ....
    }
}

mock을 사용하고 싶지 않고 실제 bean을 사용하고 싶다면 useMocks프로파일을 활성화하지 마십시오 .

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = {Application.class})
@WebIntegrationTest
public class AnotherIntegrationTestIT {

    @Inject
    private MyBean myBean; //It will be the real implementation!


    @Test
    public void test() {
        ....
    }
}


답변

Mockito는 1.8.3 부터 @InjectMocks-이것은 매우 유용합니다. 내 JUnit 테스트는 @RunWithMockitoJUnitRunner나는 구축 @Mock개인 회원이 주석 때 모든 주입되는 클래스에 대한 모든 종속성이 테스트되고 만족 객체 @InjectMocks.

I 만 지금은 통합 테스트합니다.@RunWithSpringJUnit4Runner

List<T>스프링과 같은 방식으로 주사 할 수없는 것 같습니다 . 를 만족하는 Mock 객체 만 찾고 Mock 객체 List목록을 삽입하지 않습니다. 해결 방법 @Spy은 수동으로 인스턴스화 된 목록에 대해 를 사용하고 단위 테스트를 위해 모의 객체를 해당 목록에 수동으로 추가하는 것입니다. 어쩌면 그것은 의도적이었습니다. 왜냐하면 그것은 함께 조롱 된 것에주의를 기울여야했기 때문입니다.


답변

업데이트 : 이제이 문제에 대한 더 나은 해결책이 있습니다. 다른 답변을 먼저 고려하십시오.

나는 결국 그의 블로그에서 ronen이 이에 대한 답변을 찾았습니다. 내가 겪고있는 문제는 Mockito.mock(Class c)의 반환 유형을 선언하는 메서드 때문 입니다 Object. 결과적으로 Spring은 팩토리 메소드 리턴 유형에서 Bean 유형을 유추 할 수 없습니다.

Ronen의 솔루션FactoryBean모의를 반환 하는 구현 을 만드는 것입니다. FactoryBean인터페이스는 봄이 공장 콩으로 만든 개체의 유형을 쿼리 할 수 있습니다.

내 조롱 된 콩 정의는 다음과 같습니다.

<bean id="mockDaoFactory" name="dao" class="com.package.test.MocksFactory">
    <property name="type" value="com.package.Dao" />
</bean>