junit 테스트 클래스에서 Spring 애플리케이션 컨텍스트 재사용


84

많은 JUnit 테스트 케이스 (통합 테스트)가 있으며 논리적으로 서로 다른 테스트 클래스로 그룹화됩니다.

http://static.springsource.org/spring/docs/current/spring-framework-reference에 언급 된대로 테스트 클래스 당 한 번 Spring 애플리케이션 컨텍스트를로드하고 JUnit 테스트 클래스의 모든 테스트 케이스에 재사용 할 수 있습니다 . /html/testing.html

그러나 우리는 JUnit 테스트 클래스 묶음에 대해 Spring 애플리케이션 컨텍스트를 한 번만로드하는 방법이 있는지 궁금했습니다.

FWIW에서는 Spring 3.0.5, JUnit 4.5를 사용하고 Maven을 사용하여 프로젝트를 빌드합니다.


5
아래의 모든 답변은 훌륭하지만 context.xml이 없습니다. 망각으로가는 길에 주석을 달았나요? context.xml없이이 작업을 수행하는 방법은 무엇입니까?
markthegrea

2
솔루션에 대한 답을 찾았습니까? 나는 같은 문제가 있으며 주석과 Spring Boot로 이것을 끝내고 싶습니다.
AleksandarT

답변:


96

예, 이것은 완벽하게 가능합니다. locations테스트 클래스에서 동일한 속성 을 사용하기 만하면됩니다.

@ContextConfiguration(locations = "classpath:test-context.xml")

Spring은 locations속성 별로 애플리케이션 컨텍스트를 캐시 하므로 locations동일한 컨텍스트가 두 번째로 나타나면 Spring은 새 컨텍스트를 생성하는 대신 동일한 컨텍스트를 사용합니다.

이 기능에 대한 기사를 작성했습니다 : Speeding up Spring integration tests . 또한 Spring 문서에서 자세히 설명합니다 : 9.3.2.1 컨텍스트 관리 및 캐싱 .

이것은 흥미로운 의미를 가지고 있습니다. Spring은 JUnit이 언제 완료되었는지 알지 못하기 때문에 모든 컨텍스트를 영원히 캐시하고 JVM 종료 후크를 사용하여 닫습니다. 이 동작 (특히 다른을 가진 테스트 클래스가 많은 경우 locations)은 과도한 메모리 사용, 메모리 누수 등을 초래할 수 있습니다. 컨텍스트 캐싱의 또 다른 이점입니다.


아! 그것을 깨닫지 못했습니다. 우리는 오랫동안이 접근 방식을 따라 왔으며 테스트 실행의 긴 지속 시간을 모든 테스트 클래스의 스프링 컨텍스트로드에 돌 렸습니다. 지금 신중하게 확인하겠습니다. 감사.
Ramesh 2011

1
차라리 봄에는 테스트 케이스의 실행 순서에 대한 지식이 없다고 말하고 싶습니다. 그 결과 컨텍스트가 나중에 필요한지 아니면 폐기 될 수 있는지 알 수 없습니다.
philnate

1
이것이 실제로 어떻게 사실 일 수 있는지 모르겠습니다. Eclipse / JUnit은 Run As / JUnit 테스트를 수행 할 때마다 환경을 시작하는 데 2 ​​분을 소비합니다. 캐시 된 것이 있으면 이런 일이 발생하지 않습니다.
user1944491

3
이것이 컨텍스트 정의를 위해 XML을 사용하는 대신 주석을 통해 완전히 수행 될 수 있는지 여부에 대한 아이디어가 있습니까? 나는 문서와 여기에서 그것에 대해 많이 검색했지만 불가능하다고 생각하게 만드는 것을 찾지 못했습니다.
Jean-François Savard

이니셜 라이저가 있으면 이것이 사실이 아닙니까? 내 수업의 모든 테스트를 초기화하고 있습니다
Kalpesh Soni

26

Tomasz Nurkiewicz의 답변에 추가하기 위해 Spring 3.2.2부터 @ContextHierarchy주석을 사용하여 별도의 연관된 다중 컨텍스트 구조를 가질 수 있습니다. 이는 여러 테스트 클래스가 메모리 내 데이터베이스 설정 (예 : 데이터 소스, EntityManagerFactory, tx 관리자 등)을 공유하려는 경우에 유용합니다.

예를 들면 :

@ContextHierarchy({
  @ContextConfiguration("/test-db-setup-context.xml"),
  @ContextConfiguration("FirstTest-context.xml")
})
@RunWith(SpringJUnit4ClassRunner.class)
public class FirstTest {
 ...
}

@ContextHierarchy({
  @ContextConfiguration("/test-db-setup-context.xml"),
  @ContextConfiguration("SecondTest-context.xml")
})
@RunWith(SpringJUnit4ClassRunner.class)
public class SecondTest {
 ...
}

이 설정을 통해 "test-db-setup-context.xml"을 사용하는 컨텍스트는 한 번만 생성되지만 그 안의 빈은 개별 단위 테스트의 컨텍스트에 주입 될 수 있습니다.

매뉴얼에 대한 추가 정보 : http://docs.spring.io/spring/docs/current/spring-framework-reference/html/testing.html#testcontext-ctx-management ( " context hierarchy " 검색 )


나는 다중 모듈 maven을 가지고 있으며 서비스 모듈에서 데이터베이스 설정을 피하려고합니다 (이미 dataaccess 모듈 테스트로로드되었으므로) 나를 위해 작동하지 않습니다!
Muhammad Hewedy 2014

5
이것은 나를 위해 일했습니다! 감사. 명확하게 말하자면 @ContextHierarchy 주석이 없으면 스프링은 각 테스트에 대해 내 db를로드합니다. "classes"매개 변수를 사용하고 있습니다. @ContextConfiguration (classes = {JpaConfigTest.class, ...
Brel

5
이것이 컨텍스트 정의를 위해 XML을 사용하는 대신 주석을 통해 완전히 수행 될 수 있는지 여부에 대한 아이디어가 있습니까? 나는 문서와 여기에서 그것에 대해 많이 검색했지만 불가능하다고 생각하게 만드는 것을 찾지 못했습니다.
Jean-François Savard

1
@ Jean-FrançoisSavard (XML 대신 annotationsw를 통해) 검색에서 운이 좋았습니까?
javadev

@javadev 나는 이것이 당신이 찾고있는 것이기
Raviteja Gubba

1

기본적으로 Spring은 다른 테스트 클래스에 걸쳐 동일한 애플리케이션 컨텍스트 구성이있는 경우이를 구성 할 수있을만큼 똑똑합니다. 예를 들어 다음과 같이 두 개의 클래스 A와 B가 있다고 가정합니다.

@ActiveProfiles("h2")
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class A {

    @MockBean
    private C c;
    //Autowired fields, test cases etc...
}

@ActiveProfiles("h2")
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class B {

    @MockBean
    private D d;
    //Autowired fields, test cases etc...
}

이 예제에서 클래스 A는 빈 C를 모의하는 반면 클래스 B는 빈 D를 모의합니다. 따라서 스프링은 이것을 두 개의 다른 구성으로 간주하므로 클래스 A에 대해 한 번, 클래스 B에 대해 한 번 애플리케이션 컨텍스트를로드합니다.

대신 스프링이이 두 클래스간에 애플리케이션 컨텍스트를 공유하도록하려면 다음과 같이 보여야합니다.

@ActiveProfiles("h2")
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class A {

    @MockBean
    private C c;

    @MockBean
    private D d;
    //Autowired fields, test cases etc...
}

@ActiveProfiles("h2")
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class B {

    @MockBean
    private C c;

    @MockBean
    private D d;
    //Autowired fields, test cases etc...
}

이와 같이 클래스를 연결하면 Spring은 테스트 스위트에서 먼저 실행되는 클래스에 따라 클래스 A 또는 B에 대해 애플리케이션 컨텍스트를 한 번만로드합니다. 이것은 여러 테스트 클래스에 걸쳐 복제 될 수 있지만 기준은 테스트 클래스를 다르게 사용자 정의해서는 안된다는 것입니다. 테스트 클래스가 다른 것과 다른 결과를 초래하는 모든 사용자 정의 (스프링의 관점에서)는 결국 스프링까지 다른 애플리케이션 컨텍스트를 생성하게됩니다.


0

아래와 같이 구성 클래스를 만듭니다.

@ActiveProfiles("local")
@RunWith(SpringJUnit4ClassRunner.class )
@SpringBootTest(classes ={add your spring beans configuration classess})
@TestPropertySource(properties = {"spring.config.location=classpath:application"})
@ContextConfiguration(initializers = ConfigFileApplicationContextInitializer.class)
public class RunConfigration {

    private ClassLoader classloader = Thread.currentThread().getContextClassLoader();

    private static final Logger LOG = LoggerFactory.getLogger(S2BXISINServiceTest.class);


    //auto wire all the beans you wanted to use in your test classes
    @Autowired
    public XYZ xyz;
    @Autowired
    public ABC abc;


    }



Create your test suite like below



@RunWith(Suite.class)
@Suite.SuiteClasses({Test1.class,test2.class})
public class TestSuite extends RunConfigration {

    private ClassLoader classloader = Thread.currentThread().getContextClassLoader();

    private static final Logger LOG = LoggerFactory.getLogger(TestSuite.class);


}

아래와 같이 테스트 클래스를 만듭니다.

public class Test1 extends RunConfigration {


  @Test
    public void test1()
    {
    you can use autowired beans of RunConfigration classes here 
    }

}


public class Test2a extends RunConfigration {

     @Test
    public void test2()
    {
    you can use autowired beans of RunConfigration classes here 
    }


}

0

한 가지 주목할만한 점은 @SpringBootTests를 사용하지만 다시 사용 use @MockBean in different test classes하면 Spring은 모든 테스트에 대해 애플리케이션 컨텍스트를 재사용 할 방법이 없다는 것입니다.

해결책은 to move all @MockBean into an common abstract class문제를 해결하는 것입니다.

@SpringBootTests(webEnvironment = WebEnvironment.RANDOM_PORT, classes = Application.class)
public abstract class AbstractIT {

   @MockBean
   private ProductService productService;

   @MockBean
   private InvoiceService invoiceService;

}

그런 다음 테스트 클래스는 다음과 같이 볼 수 있습니다.

public class ProductControllerIT extends AbstractIT {
   // please don't use @MockBean here
   @Test
   public void searchProduct_ShouldSuccess() {
   }

}

public class InvoiceControllerIT extends AbstractIT {
   // please don't use @MockBean here
   @Test
   public void searchInvoice_ShouldSuccess() {
   }

}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.