문제

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

이 모든 것은 애플리케이션 컨텍스트의 특정 빈을 재정의하고 원할 때 컨텍스트를 새로 고치고 싶다는 것을 의미합니다.이 작업을 수행하는 동안 하나(또는 여러)의 원본 xml bean 정의 파일에 있는 bean의 작은 부분만 재정의하고 싶습니다.나는 그것을 할 쉬운 방법을 찾을 수 없습니다.Spring은 단위 테스트 친화적인 프레임워크로 항상 간주되므로 여기서 뭔가 빠졌을 것입니다.

어떻게 할 수 있는지 아이디어가 있나요?

감사해요.

도움이 되었습니까?

해결책

나는 사용자 정의 TestClass와 spring 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을 재정의합니다. 일반 위치는 다를 수 있습니다.

하지만...나는 모의 빈과 비 모의 빈을 절대 섞지 않을 것입니다. 애플리케이션이 오래되면 문제를 추적하기가 어렵습니다.

다른 팁

스프링이 테스트 친화적이라고 설명되는 이유 중 하나는 새로운 또는 단위 테스트에서 물건을 모의합니다.

또는 다음 설정을 사용하여 큰 성공을 거두었으며 귀하가 원하는 것과 매우 가깝다고 생각합니다. 강하게 추천하세요:

다양한 컨텍스트에서 다양한 구현이 필요한 모든 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이 많으면 기본적으로 컴포넌트 스캔 정의만 포함하는 몇 가지 새로운 spring xml 파일을 만들어야 할 것입니다.일반적으로 이러한 파일을 일반 @ContextConfiguration 목록에 추가합니다.그 이유는 컨텍스트 스캔의 다른 구성으로 끝나는 경우가 많기 때문입니다(저를 믿으세요. ~ 할 것이다 웹 테스트를 수행하는 경우 주석을 1개 이상 추가하여 4개의 관련 조합을 만듭니다.)

그런 다음 기본적으로

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

이 설정은 ~ 아니다 스텁/라이브 데이터를 교대로 조합할 수 있습니다.우리는 이것을 시도했고 그로 인해 엉망이 된 것 같습니다. 누구에게도 추천하지 않습니다. ;) 우리는 전체 스텁 세트 또는 전체 라이브 서비스 세트를 여관에 연결합니다.

일반적으로 종속성이 상당히 큰 항목 근처에서 GUI를 테스트할 때 우리는 주로 자동 연결 스텁 종속성을 사용합니다.코드의 깔끔한 영역에서는 보다 정기적인 단위 테스트를 사용합니다.

우리 시스템에는 구성 요소 스캔을 위한 다음과 같은 XML 파일이 있습니다.

  • 일반 웹 제작을 위한
  • 스텁만으로 웹을 시작하는 경우
  • 통합 테스트용(junit)
  • 단위 테스트용(junit)
  • 셀레늄 웹 테스트용(junit)

이는 애플리케이션을 시작할 수 있는 시스템 전체 구성이 완전히 5개 있다는 것을 의미합니다.주석만 사용하기 때문에 Spring은 우리가 연결하려는 단위 테스트도 자동 연결하기에 충분히 빠릅니다.나는 이것이 전통적이지 않다는 것을 알고 있지만 정말 훌륭합니다.

통합 테스트는 전체 라이브 설정으로 실행되며 한두 번은 정말 실용적이며 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);
     }


   }
}

나는 테스트 순수주의자들이 이것 때문에 나를 온통 괴롭힐 것이라는 것을 알고 있습니다.그러나 때로는 대안이 정말 추악할 때 매우 실용적인 솔루션이 매우 우아한 것으로 판명되는 경우도 있습니다.다시 말하지만 일반적으로 GUI 근처 지역에 있습니다.

이것 좀 봐 @InjectedMock 주석이 포함된 튜토리얼

그것은 나에게 많은 시간을 절약해주었다.그냥 사용하세요

@Mock
SomeClass mockedSomeClass

@InjectMock
ClassUsingSomeClass service

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

그러면 모든 문제가 해결됩니다.Mockito는 스프링 종속성 주입을 모의로 대체합니다.방금 직접 사용했는데 훌륭하게 작동합니다.

여기에는 매우 복잡하고 강력한 솔루션이 나열되어 있습니다.

그러나 거기에는 훨씬 더 단순해졌습니다. Stas가 요청한 작업을 수행하는 방법으로, 테스트 메서드의 코드 한 줄 외에는 아무것도 수정하지 않습니다.자동 배선된 종속성, 비공개 및 보호 필드에 대해 단위 테스트와 Spring 통합 테스트 모두에서 작동합니다.

여기있어:

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

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

@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() {
        ...
    }
}

이를 통해 실제 구성 파일과 테스트 구성 파일을 쉽게 혼합하고 일치시킬 수 있습니다.

예를 들어, 최대 절전 모드를 사용할 때 하나의 구성 파일에 sessionFactory 빈이 있고(테스트와 기본 앱 모두에서 사용됨) by dataSource 빈이 다른 구성 파일에 있을 수 있습니다(하나는 DriverManagerDataSource를 내부에 사용할 수 있음). 메모리 db, 다른 하나는 JNDI 조회를 사용할 수 있습니다).

하지만 확실히 주의하세요 @cletus의 경고 ;-)

쉬운.단위 테스트에는 사용자 정의 애플리케이션 컨텍스트를 사용합니다.또는 전혀 사용하지 않고 수동으로 Bean을 생성하고 주입합니다.

제가 보기에는 테스트 범위가 너무 광범위할 것 같습니다.단위 테스트는 단위 테스트에 관한 것입니다.Spring 빈은 단위의 아주 좋은 예입니다.이를 위해서는 전체 애플리케이션 컨텍스트가 필요하지 않습니다.단위 테스트 수준이 너무 높아서 수백 개의 빈, 데이터베이스 연결 등이 필요한 경우 바로 다음 변경 시 중단될 매우 취약한 단위 테스트를 갖게 되며 유지 관리가 어렵고 실제로는 그렇지 않습니다. 많은 가치를 추가하지 마십시오.

당신은 사용할 수 있습니다 수입 prod bean에 로드하고 원하는 것을 재정의하는 기능을 테스트 앱 컨텍스트에 추가하세요.예를 들어, 내 프로덕션 데이터 소스는 일반적으로 JNDI 조회를 통해 획득되지만 테스트할 때는 DriverManager 데이터 소스를 사용하므로 테스트하기 위해 앱 서버를 시작할 필요가 없습니다.

나는 duffymo의 답변에 쌓을 만큼 평판이 좋지는 않지만, 단지 그의 답변이 나에게 "올바른" 답변이었다고 말하고 싶었을 뿐입니다.

사용자 정의 applicationContext.xml을 사용하여 단위 테스트 설정에서 FileSystemXmlApplicationContext를 인스턴스화합니다.해당 사용자 정의 XML의 상단에서 duffymo가 지시하는 대로 수행하십시오.그런 다음 가져오기에서 선언된 ID를 대체할 모의 빈, JNDI가 아닌 데이터 소스 등을 선언합니다.

나에게는 꿈처럼 일했습니다.

테스트 컨텍스트를 사용할 필요가 없습니다(XML 기반인지 Java 기반인지는 중요하지 않습니다).Spring boot 1.4부터 새로운 주석이 제공됩니다. @MockBean 이는 Spring Bean의 조롱 및 감시에 대한 기본 지원을 도입했습니다.

아마도 빈에 한정자를 사용할 수 있을까요?별도의 애플리케이션 컨텍스트에서 모형화하려는 Bean을 재정의하고 한정자 "test"로 레이블을 지정합니다.단위 테스트에서 빈을 연결할 때 모형을 사용하려면 항상 "테스트" 한정자를 지정하세요.

나도 같은 일을 하고 싶고 우리는 그것이 필수적이라고 생각합니다.

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

예를 들어 Y 유형의 빈을 모의하고 싶다고 가정해 보겠습니다.우리가 하는 일은 해당 종속성을 갖는 모든 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 구성 파일을 생성하려는 경우 모의 빈을 사용하여 새 팩토리를 생성하고 기본 팩토리를 이 팩토리의 상위 팩토리로 만드는 방법이 있습니다.그런 다음 새 하위 팩토리에서 모든 Bean을 로드했는지 확인하십시오.이를 수행할 때 하위 팩토리는 빈 ID가 동일할 때 상위 팩토리의 빈을 대체합니다.

이제 내 테스트에서 내가 할 수 있다면 프로그래밍 방식으로 공장을 만들면 정말 좋을 것 같아요.XML을 사용하는 것은 너무 번거롭습니다.코드를 사용하여 하위 팩토리를 만들려고 합니다.그런 다음 각 테스트는 원하는 방식으로 공장을 구성할 수 있습니다.그런 공장이 작동하지 않을 이유가 없습니다.

봄 재주입 빈을 모의로 대체하도록 설계되었습니다.

OP 이후로 다음과 같은 결과가 나왔습니다. 스프링오키토

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top