복붙노트

[SPRING] 단위 테스트 환경에서 스프링 빈 재정의

SPRING

단위 테스트 환경에서 스프링 빈 재정의

우리는 어플리케이션 용도로 Spring을 사용하고, 단위 테스트를 위해 Spring Testing 프레임 워크를 사용합니다. 그러나 우리는 작은 문제가 있습니다. 애플리케이션 코드는 클래스 경로의 위치 목록 (xml 파일)에서 Spring 애플리케이션 컨텍스트를로드합니다. 그러나 우리가 단위 테스트를 실행할 때, Spring Bean의 일부가 본격적인 구현 클래스 대신에 mock이되기를 바랍니다. 또한 일부 단위 테스트에서는 일부 빈이 모의 객체가되기를 원하지만 다른 단위 테스트에서는 응용 프로그램의 여러 계층을 테스트 할 때 다른 빈이 모의 객체가되기를 원합니다.

이 모든 것은 응용 프로그램 컨텍스트의 특정 빈을 재정의하고 원하는 경우 컨텍스트를 새로 고치려 함을 의미합니다. 이 작업을 수행하는 동안 하나 또는 일부 원본 xml bean 정의 파일에있는 콩의 작은 부분 만 재정의하려고합니다. 나는 그것을 할 수있는 쉬운 방법을 찾을 수 없습니다. Spring은 친숙한 프레임 워크를 테스트하는 단위이므로 항상 여기에 뭔가 빠져 있어야합니다.

어떻게 할 생각이 있습니까?

감사.

해결법

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

    1.커스텀 TestClass와 스프링 bean.xml의 위치에 대한 쉬운 규칙을 제안 할 것이다.

    커스텀 TestClass와 스프링 bean.xml의 위치에 대한 쉬운 규칙을 제안 할 것이다.

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = {
        "classpath*:spring/*.xml",
        "classpath*:spring/persistence/*.xml",
        "classpath*:spring/mock/*.xml"})
    @Transactional
    @TestExecutionListeners({
        DependencyInjectionTestExecutionListener.class,
        TransactionalTestExecutionListener.class,
        DirtiesContextTestExecutionListener.class})
    public abstract class AbstractHibernateTests implements ApplicationContextAware 
    {
    
        /**
         * Logger for Subclasses.
         */
        protected final Logger LOG = LoggerFactory.getLogger(getClass());
    
        /**
         * The {@link ApplicationContext} that was injected into this test instance
         * via {@link #setApplicationContext(ApplicationContext)}.
         */
        protected ApplicationContext applicationContext;
    
        /**
         * Set the {@link ApplicationContext} to be used by this test instance,
         * provided via {@link ApplicationContextAware} semantics.
         */
        @Override
        public final void setApplicationContext(
                final ApplicationContext applicationContext) {
            this.applicationContext = applicationContext;
        }
    }
    

    지정된 위치에 mock-bean.xml이 있으면 "정상"위치에있는 모든 "실제"bean.xml을 덮어 씁니다. 보통 위치는 다를 수 있습니다

    하지만 ... 모의 (mock)와 모의가 아닌 (non-mock) 빈은 결코 섞이지 않을 것입니다. 응용 프로그램이 오래 갈수록 문제를 추적하기가 어렵습니다.

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

    2.스프링이 테스트 친화적 인 것으로 묘사 된 이유 중 하나는 단위 테스트에서 새로운 것이나 모의 물건에 쉽게 적용될 수 있기 때문입니다.

    스프링이 테스트 친화적 인 것으로 묘사 된 이유 중 하나는 단위 테스트에서 새로운 것이나 모의 물건에 쉽게 적용될 수 있기 때문입니다.

    또는 다음 설정을 사용하여 큰 성공을 거두었습니다. 원하는대로 매우 가깝다고 생각합니다. 강력하게 추천합니다.

    다른 컨텍스트에서 다른 구현이 필요한 모든 bean의 경우 주석 기반 배선으로 전환하십시오. 다른 사람들은 그대로 남겨 둘 수 있습니다.

    다음 주석 집합 구현

     <context:component-scan base-package="com.foobar">
         <context:include-filter type="annotation" expression="com.foobar.annotations.StubRepository"/>
         <context:include-filter type="annotation" expression="com.foobar.annotations.TestScopedComponent"/>
         <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
     </context:component-scan>
    

    그런 다음 라이브 구현에 @Repository, @StubRepository로 작성된 스텁 구현, @TestScopedComponent를 사용하여 유닛 테스트 픽스처에 존재해야하는 모든 코드에 주석을 추가한다. 몇 가지 특수 효과가 필요할 수도 있지만 위대한 시작입니다.

    spring.xml이 많은 경우 기본적으로 구성 요소 검색 정의 만 포함하는 몇 가지 새 xml 파일을 만들어야 할 수 있습니다. 일반적으로 이러한 파일을 일반 @ContextConfiguration 목록에 추가합니다. 그 이유는 자주 컨텍스트 스캔의 다른 구성으로 끝나기 때문입니다 (신뢰할 수있는 4 가지 관련 조합을 만드는 웹 테스트를 수행하는 경우 적어도 1 개의 주석을 추가로 작성해야합니다)

    그럼 기본적으로

    @ContextConfiguration(locations = { "classpath:/path/to/root-config.xml" })
    @RunWith(SpringJUnit4ClassRunner.class)
    

    이 설정에서는 스텁 / 라이브 데이터의 조합을 번갈아 사용할 수 없습니다. 우리는 이것을 시도했고, 나는 그것이 아무도 추천하지 않는 혼란을 초래했다고 생각한다.) 우리는 여관에 전체 스텁 세트 또는 실제 서비스 세트를 연결한다.

    종속성이 일반적으로 상당히 중요한 곳에 GUI를 테스트 할 때 주로 자동 배선 스텁 종속성을 사용합니다. 코드의 더 깨끗한 부분에서는 좀 더 규칙적인 단위 테스트를 사용합니다.

    우리 시스템에서 우리는 component-scan을 위해 다음과 같은 XML 파일을 가지고있다.

    즉, 응용 프로그램을 시작할 수있는 시스템 차원의 구성이 5 가지입니다. 주석 만 사용하기 때문에 스프링은 우리가 유선을 원하는 유닛 테스트를 autowire 할만큼 빠릅니다. 나는 이것이 전통이 없다는 것을 알고 있지만, 정말 훌륭합니다.

    통합 테스트는 전체 라이브 설정으로 실행되며 1 ~ 2 회는 실제로 실용적인 것으로 결정하고 5 개의 라이브 배선과 단일 모의 마스크를 갖기를 원합니다.

    public class HybridTest {
       @Autowired
       MyTestSubject myTestSubject;
    
    
       @Test
       public void testWith5LiveServicesAndOneMock(){
         MyServiceLive service = myTestSubject.getMyService();
         try {
              MyService mock = EasyMock.create(...)
              myTestSubject.setMyService( mock);
    
               .. do funky test  with lots of live but one mock object
    
         } finally {
              myTestSubject.setMyService( service);
         }
    
    
       }
    }
    

    나는 시험 주부들이 이것을 위해 나를 온통 통과 할 것이라는 것을 안다. 그러나 때로는 대안이 정말 못 생길 때 매우 우아한 것으로 드러나는 매우 실용적인 솔루션 일 수도 있습니다. 다시 말하지만 대체로 그 근처에 있습니다.

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

    3.@InjectedMock 주석을 사용하여이 자습서를 참조하십시오.

    @InjectedMock 주석을 사용하여이 자습서를 참조하십시오.

    그것은 많은 시간을 절약 해주었습니다. 너는 단지 사용한다.

    @Mock
    SomeClass mockedSomeClass
    
    @InjectMock
    ClassUsingSomeClass service
    
    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
    }
    

    모든 문제가 해결되었습니다. Mockito는 스프링 의존성 주입을 모의로 대체 할 것입니다. 방금 나 자신을 사용했고 훌륭하게 작동합니다.

  4. ==============================

    4.여기에 나열된 몇 가지 매우 복잡하고 강력한 솔루션이 있습니다.

    여기에 나열된 몇 가지 매우 복잡하고 강력한 솔루션이 있습니다.

    그러나 FAR, FAR은 Stas가 요청한 것을 수행하는 더 간단한 방법입니다.이 방법은 테스트 방법에서 한 줄의 코드를 수정하는 것이 아닙니다. autowired 종속성, 개인 필드 및 보호 된 필드에 대해 단위 테스트 및 스프링 통합 테스트에 모두 적합합니다.

    여기있어:

    junitx.util.PrivateAccessor.setField(testSubject, "fieldName", mockObject);
    
  5. ==============================

    5.또한 단위 테스트를 작성하여 조회가 전혀 필요하지 않도록 할 수 있습니다.

    또한 단위 테스트를 작성하여 조회가 전혀 필요하지 않도록 할 수 있습니다.

    @ContextConfiguration(locations = { "classpath:/path/to/test-config.xml" })
    @RunWith(SpringJUnit4ClassRunner.class)
    public class MyBeanTest {
    
        @Autowired
        private MyBean myBean; // the component under test
    
        @Test
        public void testMyBean() {
            ...
        }
    }
    

    이렇게하면 실제 구성 파일을 테스트 구성 파일과 쉽게 섞어서 일치시킬 수 있습니다.

    예를 들어, hibernate를 사용할 때 하나의 설정 파일에 sessionFactory bean을 가지고 있고 다른 설정 파일에 dataSource bean을 가지고있다. (하나는 DriverManagerDataSource를 in- 메모리 db, 다른 하나는 JNDI 조회를 사용할 수 있음).

    그러나 확실히 @ cletus의 경고에주의를 기울이십시오 ;-)

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

    6.쉬운. 단위 테스트를 위해 사용자 지정 응용 프로그램 컨텍스트를 사용합니다. 또는 전혀 사용하지 않고 직접 콩을 만들고 주입합니다.

    쉬운. 단위 테스트를 위해 사용자 지정 응용 프로그램 컨텍스트를 사용합니다. 또는 전혀 사용하지 않고 직접 콩을 만들고 주입합니다.

    당신의 테스트가 너무 광범위 할 것 같아요. 단위 테스트는 단위 테스트에 관한 것입니다. Spring 빈은 단위의 꽤 좋은 예입니다. 이를 위해서는 전체 애플리케이션 컨텍스트가 필요하지 않습니다. 만약 당신의 단위 테스트가 수백 개의 콩, 데이터베이스 연결 등을 필요로하는 매우 높은 수준이라면 다음번의 변화를 깨뜨릴 수있는 깨지기 쉬운 단위 테스트를 가지고 유지하기가 어려울 것이며 정말로 ' 많은 가치를 더합니다.

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

    7.테스트 응용 프로그램 컨텍스트에서 가져 오기 기능을 사용하여 prod beans에로드하고 원하는 bean을 재정의 할 수 있습니다. 예를 들어 내 제품 데이터 소스는 일반적으로 JNDI 조회를 통해 수집되지만, 테스트 할 때 DriverManager 데이터 소스를 사용하므로 테스트 할 앱 서버를 시작할 필요가 없습니다.

    테스트 응용 프로그램 컨텍스트에서 가져 오기 기능을 사용하여 prod beans에로드하고 원하는 bean을 재정의 할 수 있습니다. 예를 들어 내 제품 데이터 소스는 일반적으로 JNDI 조회를 통해 수집되지만, 테스트 할 때 DriverManager 데이터 소스를 사용하므로 테스트 할 앱 서버를 시작할 필요가 없습니다.

  8. ==============================

    8.나는 두피모의 대답에 쌓여있는 평판 포인트를 얻지 못했지만, 나는 그저 나에게 말을 걸고 나에게 "옳은"대답을하고 싶었다.

    나는 두피모의 대답에 쌓여있는 평판 포인트를 얻지 못했지만, 나는 그저 나에게 말을 걸고 나에게 "옳은"대답을하고 싶었다.

    사용자 정의 applicationContext.xml을 사용하여 유닛 테스트 설정에서 FileSystemXmlApplicationContext를 인스턴스화합니다. 그 커스텀 XML에서 duffymo가 가리키는대로 맨 위를 수행하십시오. 그런 다음 가져 오기에서 선언 된 ID를 무시하는 mock bean, 비 JNDI 데이터 소스 등을 선언하십시오.

    나를 위해 꿈처럼 일했다.

  9. ==============================

    9.테스트 컨텍스트를 사용할 필요가 없습니다 (XML 또는 Java 기반은 중요하지 않음). 스프링 부트 1.4 이후로 @MockBean이라는 새로운 주석이 사용 가능해졌습니다. @MockBean은 조롱과 스프링 콩의 스파이에 대한 기본 지원을 도입했습니다.

    테스트 컨텍스트를 사용할 필요가 없습니다 (XML 또는 Java 기반은 중요하지 않음). 스프링 부트 1.4 이후로 @MockBean이라는 새로운 주석이 사용 가능해졌습니다. @MockBean은 조롱과 스프링 콩의 스파이에 대한 기본 지원을 도입했습니다.

  10. ==============================

    10.아마도 콩에 한정어를 사용할 수 있습니까? 별도의 응용 프로그램 컨텍스트에서 모의하려는 bean을 다시 정의하고 "test"한정자로 레이블을 지정합니다. 단위 테스트에서 빈을 연결하면 모의 테스트를 사용하기 위해 항상 "test"한정자를 지정합니다.

    아마도 콩에 한정어를 사용할 수 있습니까? 별도의 응용 프로그램 컨텍스트에서 모의하려는 bean을 다시 정의하고 "test"한정자로 레이블을 지정합니다. 단위 테스트에서 빈을 연결하면 모의 테스트를 사용하기 위해 항상 "test"한정자를 지정합니다.

  11. ==============================

    11.나는 똑같은 일을하고 싶다.

    나는 똑같은 일을하고 싶다.

    우리가 사용하는 현재 메커니즘은 상당히 수동이지만 작동합니다.

    예를 들어, Y 타입의 bean을 조롱하고 싶다고하자. 우리가하는 일은 우리가 인터페이스를 구현하는 의존성을 가진 모든 bean이다. "IHasY". 이 인터페이스는

    interface IHasY {
       public void setY(Y y);
    }
    

    그런 다음 우리의 테스트에서 util 메소드를 호출합니다 ...

     public static void insertMock(Y y) {
            Map invokers = BeanFactory.getInstance().getFactory("core").getBeansOfType(IHasY.class);
            for (Iterator iterator = invokers.values().iterator(); iterator.hasNext();) {
                IHasY invoker = (IHasY) iterator.next();
                invoker.setY(y);
            }
        }
    

    나는이 새로운 의존성을 주입하기 위해 전체 XML 파일을 만들고 싶지 않고 이것이 내가 이것을 좋아하는 이유이다.

    xml 설정 파일을 만들고 싶다면 mock beans로 새로운 팩토리를 만들고 기본 팩토리를이 팩토리의 부모로 만드십시오. 새 하위 팩토리에서 모든 빈을로드했는지 확인하십시오. 이 작업을 수행 할 때 하위 팩토리는 bean id가 같을 때 상위 팩토리에있는 bean을 대체합니다.

    이제 내 테스트에서 프로그래밍 방식으로 팩토리를 만들 수 있다면 멋질 것입니다. xml을 사용하는 것은 너무 성가신 일입니다. 코드를 사용하여 해당 하위 팩토리를 만들려고합니다. 그런 다음 각 테스트는 원하는 방식으로 공장을 구성 할 수 있습니다. 그런 공장이 작동하지 않을 이유가 없습니다.

  12. ==============================

    12.spring-reinject는 빈을 모의로 대체하도록 고안되었습니다.

    spring-reinject는 빈을 모의로 대체하도록 고안되었습니다.

  13. ==============================

    13.OP가 나온 이래로 : Springockito

    OP가 나온 이래로 : Springockito

  14. from https://stackoverflow.com/questions/565334/spring-beans-redefinition-in-unit-test-environment by cc-by-sa and MIT license