봄 콩에 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를 사용하지 않거나 이전 버전을 사용하는 경우 약간 더 많은 작업을 수행해야합니다.
@Configuration
Spring 컨텍스트에 모의 객체를 주입 하는 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 테스트는 @RunWith
을 MockitoJUnitRunner
나는 구축 @Mock
개인 회원이 주석 때 모든 주입되는 클래스에 대한 모든 종속성이 테스트되고 만족 객체 @InjectMocks
.
I 만 지금은 통합 테스트합니다.@RunWith
SpringJUnit4Runner
List<T>
스프링과 같은 방식으로 주사 할 수없는 것 같습니다 . 를 만족하는 Mock 객체 만 찾고 Mock 객체 List
목록을 삽입하지 않습니다. 해결 방법 @Spy
은 수동으로 인스턴스화 된 목록에 대해 를 사용하고 단위 테스트를 위해 모의 객체를 해당 목록에 수동으로 추가하는 것입니다. 어쩌면 그것은 의도적이었습니다. 왜냐하면 그것은 함께 조롱 된 것에주의를 기울여야했기 때문입니다.
업데이트 : 이제이 문제에 대한 더 나은 해결책이 있습니다. 다른 답변을 먼저 고려하십시오.
나는 결국 그의 블로그에서 ronen이 이에 대한 답변을 찾았습니다. 내가 겪고있는 문제는 Mockito.mock(Class c)
의 반환 유형을 선언하는 메서드 때문 입니다 Object
. 결과적으로 Spring은 팩토리 메소드 리턴 유형에서 Bean 유형을 유추 할 수 없습니다.
Ronen의 솔루션 은 FactoryBean
모의를 반환 하는 구현 을 만드는 것입니다. 이 FactoryBean
인터페이스를 통해 Spring은 팩토리 빈에 의해 생성 된 객체의 유형을 쿼리 할 수 있습니다.
내 조롱 된 콩 정의는 이제 다음과 같습니다.
<bean id="mockDaoFactory" name="dao" class="com.package.test.MocksFactory">
<property name="type" value="com.package.Dao" />
</bean>
Spring 3.2부터는 더 이상 문제가되지 않습니다. Spring은 이제 일반적인 팩토리 메소드 결과의 자동 배선을 지원합니다. 이 블로그 게시물 ( http://spring.io/blog/2012/11/07/spring-framework-3-2-rc1-new-testing-features/) 에서 "Generic Factory Methods"라는 제목의 섹션을 참조 하십시오 .
핵심은 다음과 같습니다.
Spring 3.2에서는 팩토리 메소드의 일반 리턴 유형이 올바르게 추론되고 모의 유형별 자동 배선이 예상대로 작동합니다. 결과적으로 MockitoFactoryBean, EasyMockFactoryBean 또는 Springockito와 같은 사용자 정의 해결 방법은 더 이상 필요하지 않습니다.
이것은 이것이 즉시 작동해야 함을 의미합니다.
<bean id="dao" class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="com.package.Dao" />
</bean>
아래 코드는 자동 배선과 함께 작동합니다. 가장 짧은 버전은 아니지만 표준 스프링 / mockito 항아리에서만 작동해야 할 때 유용합니다.
<bean id="dao" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target"> <bean class="org.mockito.Mockito" factory-method="mock"> <constructor-arg value="com.package.Dao" /> </bean> </property>
<property name="proxyInterfaces"> <value>com.package.Dao</value> </property>
</bean>
당신이 사용하는 경우 봄> = 3.0 , 스프링스 사용해보십시오 @Configuration
애플리케이션 컨텍스트의 일부를 정의하는 주석을
@Configuration
@ImportResource("com/blah/blurk/rest-of-config.xml")
public class DaoTestConfiguration {
@Bean
public ApplicationService applicationService() {
return mock(ApplicationService.class);
}
}
@ImportResource를 사용하지 않으려면 다른 방법으로도 수행 할 수 있습니다.
<beans>
<!-- rest of your config -->
<!-- the container recognize this as a Configuration and adds it's beans
to the container -->
<bean class="com.package.DaoTestConfiguration"/>
</beans>
자세한 내용은 spring-framework-reference : Java 기반 컨테이너 구성을 살펴보십시오.
아마도 완벽한 솔루션은 아니지만 단위 테스트를 위해 DI를 수행하기 위해 스프링을 사용하지 않는 경향이 있습니다. 단일 빈 (테스트중인 클래스)에 대한 종속성은 일반적으로 지나치게 복잡하지 않으므로 테스트 코드에서 직접 주입을 수행합니다.
Mockito를 사용하여 다음을 수행 할 수 있습니다.
<bean id="stateMachine" class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="com.abcd.StateMachine"/>
</bean>
위의 접근 방식을 기반으로 몇 가지 예 게시
봄 :
@ContextConfiguration(locations = { "classpath:context.xml" })
@RunWith(SpringJUnit4ClassRunner.class)
public class TestServiceTest {
@InjectMocks
private TestService testService;
@Mock
private TestService2 testService2;
}
봄없이 :
@RunWith(MockitoJUnitRunner.class)
public class TestServiceTest {
@InjectMocks
private TestService testService = new TestServiceImpl();
@Mock
private TestService2 testService2;
}
업데이트 -새로운 답변은 https://stackoverflow.com/a/19454282/411229 입니다. 이 답변은 3.2 이전 스프링 버전의 답변에만 적용됩니다.
나는 이것에 대한보다 확실한 해결책을 찾기 위해 잠시 동안 보았다. 이 블로그 게시물은 모든 요구 사항을 다루는 것으로 보이며 Bean 선언의 순서에 의존하지 않습니다. Mattias Severson의 모든 크레딧. http://www.jayway.com/2011/11/30/spring-integration-tests-part-i-creating-mock-objects/
기본적으로 FactoryBean을 구현하십시오.
package com.jayway.springmock;
import org.mockito.Mockito;
import org.springframework.beans.factory.FactoryBean;
/**
* A {@link FactoryBean} for creating mocked beans based on Mockito so that they
* can be {@link @Autowired} into Spring test configurations.
*
* @author Mattias Severson, Jayway
*
* @see FactoryBean
* @see org.mockito.Mockito
*/
public class MockitoFactoryBean<T> implements FactoryBean<T> {
private Class<T> classToBeMocked;
/**
* Creates a Mockito mock instance of the provided class.
* @param classToBeMocked The class to be mocked.
*/
public MockitoFactoryBean(Class<T> classToBeMocked) {
this.classToBeMocked = classToBeMocked;
}
@Override
public T getObject() throws Exception {
return Mockito.mock(classToBeMocked);
}
@Override
public Class<?> getObjectType() {
return classToBeMocked;
}
@Override
public boolean isSingleton() {
return true;
}
}
다음으로 스프링 설정을 다음과 같이 업데이트하십시오.
<beans...>
<context:component-scan base-package="com.jayway.example"/>
<bean id="someDependencyMock" class="com.jayway.springmock.MockitoFactoryBean">
<constructor-arg name="classToBeMocked" value="com.jayway.example.SomeDependency" />
</bean>
</beans>
Springockito의 개발 속도 와 공개 문제 수를 살펴보면 요즘 테스트 스위트 스택에 도입하는 것이 조금 걱정됩니다. Spring 4 릴리스 이전에 마지막 릴리스가 완료된 사실은 "Spring 4와 쉽게 통합 할 수 있습니까?"와 같은 질문을합니다. 나는 그것을 시도하지 않았기 때문에 모른다. 통합 테스트에서 Spring bean을 조롱 해야하는 경우 순수한 Spring 접근 방식을 선호합니다.
일반 스프링 기능만으로 스프링 빈을 가짜로 만드는 옵션이 있습니다. 당신은 사용할 필요가 @Primary
, @Profile
그리고 @ActiveProfiles
그것에 대한 주석. 주제에 대한 블로그 게시물을 작성했습니다.
모의를 제공하는 MockFactory를 생성하기 위해 teabot과 비슷한 대답을 찾았습니다. 나는 (narkisr에 대한 링크가 죽은 이후) 모의 공장을 만들려면 다음 예를 사용 : http://hg.randompage.org/java/src/407e78aa08a0/projects/bookmarking/backend/spring/src/test/java/ org / randompage / bookmarking / backend / testUtils / MocksFactory.java
<bean id="someFacade" class="nl.package.test.MockFactory">
<property name="type" value="nl.package.someFacade"/>
</bean>
이것은 또한 Spring이 조롱 된 콩에서 주입을 해결하려는 것을 방지하는 데 도움이됩니다.
<bean id="mockDaoFactory" name="dao" class="com.package.test.MocksFactory">
<property name="type" value="com.package.Dao" />
</bean>
이 ^는 XML 파일에서 처음 / 초기 선언되면 완벽하게 작동합니다. 모키 토 1.9.0 / 봄 3.0.5
Markus T의 답변에 사용 된 접근 방식과 조롱 할 클래스를 지정할 수 ImportBeanDefinitionRegistrar
있는 사용자 정의 주석 ( @MockedBeans
) 을 찾는 간단한 도우미 구현을 조합하여 사용합니다 . 이 접근 방식을 사용하면 조롱과 관련된 상용구 코드를 사용하여 간결한 단위 테스트가 수행된다고 생각합니다.
다음은 이러한 접근 방식으로 샘플 단위 테스트를 보는 방법입니다.
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader=AnnotationConfigContextLoader.class)
public class ExampleServiceIntegrationTest {
//our service under test, with mocked dependencies injected
@Autowired
ExampleService exampleService;
//we can autowire mocked beans if we need to used them in tests
@Autowired
DependencyBeanA dependencyBeanA;
@Test
public void testSomeMethod() {
...
exampleService.someMethod();
...
verify(dependencyBeanA, times(1)).someDependencyMethod();
}
/**
* Inner class configuration object for this test. Spring will read it thanks to
* @ContextConfiguration(loader=AnnotationConfigContextLoader.class) annotation on the test class.
*/
@Configuration
@Import(TestAppConfig.class) //TestAppConfig may contain some common integration testing configuration
@MockedBeans({DependencyBeanA.class, DependencyBeanB.class, AnotherDependency.class}) //Beans to be mocked
static class ContextConfiguration {
@Bean
public ExampleService exampleService() {
return new ExampleService(); //our service under test
}
}
}
이를 위해서는 두 가지 간단한 도우미 클래스 인 사용자 지정 주석 ( @MockedBeans
)과 사용자 지정 ImportBeanDefinitionRegistrar
구현을 정의 해야합니다. @MockedBeans
주석 정의의 요구로 주석 할 @Import(CustomImportBeanDefinitionRegistrar.class)
과는 ImportBeanDefinitionRgistrar
그것의의 구성에 조롱 콩 정의를 추가해야 registerBeanDefinitions
하는 방법.
당신이 접근 방식을 좋아한다면 내 blogpost 에서 샘플 구현 을 찾을 수 있습니다 .
Kresimir Nesek의 제안에 따라 솔루션을 개발했습니다. 코드를 좀 더 깨끗하고 모듈화하기 위해 @EnableMockedBean 새 주석을 추가했습니다 .
@EnableMockedBean
@SpringBootApplication
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes=MockedBeanTest.class)
public class MockedBeanTest {
@MockedBean
private HelloWorldService helloWorldService;
@Autowired
private MiddleComponent middleComponent;
@Test
public void helloWorldIsCalledOnlyOnce() {
middleComponent.getHelloMessage();
// THEN HelloWorldService is called only once
verify(helloWorldService, times(1)).getHelloMessage();
}
}
나는 그것을 설명 하는 게시물 을 작성 했습니다.
프로젝트를 Spring Boot 1.4로 마이그레이션하는 것이 좋습니다. 그 후 새로운 주석 @MockBean
을 사용 하여com.package.Dao
오늘 나는 Mockito bean 이전에 선언 한 스프링 컨텍스트가로드되지 않았다는 것을 알았습니다. 모의 후에 이동 한 후 앱 컨텍스트가 성공적으로로드되었습니다. 조심하세요 :)
기록을 위해 모든 테스트는 픽스처를 지연 초기화하여 다음과 같이 올바르게 작동합니다.
<bean id="fixture"
class="it.tidalwave.northernwind.rca.embeddedserver.impl.DefaultEmbeddedServer"
lazy-init="true" /> <!-- To solve Mockito + Spring problems -->
<bean class="it.tidalwave.messagebus.aspect.spring.MessageBusAdapterFactory" />
<bean id="applicationMessageBus"
class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="it.tidalwave.messagebus.MessageBus" />
</bean>
<bean class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="javax.servlet.ServletContext" />
</bean>
나는 이론적 근거가 Mattias가 여기 (포스트의 맨 아래)에서 설명하는 것이라고 생각합니다. 해결책은 콩이 선언 된 순서를 변경하고 있다고 가정합니다. 게으른 초기화는 끝 부분에 픽스처가 선언 된 "정렬"입니다.
Controller Injection을 사용하는 경우 지역 변수가 "최종"이 아닌지 확인하십시오
참고 URL : https://stackoverflow.com/questions/2457239/injecting-mockito-mocks-into-a-spring-bean
'Programing' 카테고리의 다른 글
#pragma marks의 의미는 무엇입니까? (0) | 2020.03.26 |
---|---|
왜 'x'( 'x')가 'x'== 'x'보다 빠릅니까? (0) | 2020.03.26 |
Visibility.Collapsed와 Visibility.Hidden의 차이점 (0) | 2020.03.26 |
'else if'의 올바른 구문은 무엇입니까? (0) | 2020.03.26 |
git-mv의 목적은 무엇입니까? (0) | 2020.03.26 |