在junit测试类中重用spring应用程序上下文

Ram*_*esh 73 junit spring spring-test junit4

我们有一堆JUnit测试用例(集成测试),它们在逻辑上分为不同的测试类.

我们能够为每个测试类加载一次Spring应用程序上下文,并将其重用于JUnit测试类中的所有测试用例,如http://static.springsource.org/spring/docs/current/spring-framework-reference中所述./html/testing.html

但是,我们只是想知道是否有一种方法只为一堆JUnit测试类加载一次Spring应用程序上下文.

FWIW,我们使用Spring 3.0.5,JUnit 4.5并使用Maven来构建项目.

Tom*_*icz 86

是的,这是完全可能的.您所要做的就是locations在测试类中使用相同的属性:

@ContextConfiguration(locations = "classpath:test-context.xml")
Run Code Online (Sandbox Code Playgroud)

Spring按locations属性缓存应用程序上下文,因此如果locations第二次出现相同的情况,Spring会使用相同的上下文而不是创建新的上下文.

我写了一篇关于此功能的文章:加速Spring集成测试.另外,它在Spring文档中有详细描述:9.3.2.1上下文管理和缓存.

这有一个有趣的含义.因为Spring不知道JUnit什么时候完成,所以它会永远缓存所有上下文并使用JVM shutdown hook关闭它们.这种行为(特别是当你有很多不同的测试类时locations)可能导致过多的内存使用,内存泄漏等.缓存上下文的另一个好处.

  • 知道这是否可以通过注释完全完成而不是使用 XML 进行上下文定义吗?我在文档和 SO 上搜索了很多关于它的内容,但找不到任何让我认为这是不可能的。 (3认同)

ger*_*tan 25

要添加到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 {
 ...
}
Run Code Online (Sandbox Code Playgroud)

通过此设置,使用"test-db-setup-context.xml"的上下文将只创建一次,但其中的bean可以注入到单个单元测试的上下文中

更多的手册:http://docs.spring.io/spring/docs/current/spring-framework-reference/html/testing.html#testcontext-ctx-management(搜索" 上下文结构 ")

  • 是否可以通过注释完全完成,而不是使用XML进行上下文定义?我在文档中搜索了很多关于它的内容,在这里搜索了SO但却找不到任何导致我认为不可能的内容. (4认同)
  • 这对我有用!谢谢.为了清楚起见,没有@ContextHierarchy注释,spring会为每个测试加载我的数据库.我正在使用"类"参数:@ContextConfiguration(classes = {JpaConfigTest.class,... (3认同)

tur*_*ong 7

值得注意的一点是,如果我们再次使用 @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;

}
Run Code Online (Sandbox Code Playgroud)

然后测试类如下所示

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

}
Run Code Online (Sandbox Code Playgroud)


Sau*_*our 6

基本上,如果您在不同的测试类中具有相同的应用程序上下文配置,那么 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...
}
Run Code Online (Sandbox Code Playgroud)

在此示例中,类 A 模拟 bean C,而类 B 模拟 bean D。因此,spring 将它们视为两种不同的配置,因此将为类 A 加载应用程序上下文一次,为类 B 加载一次应用程序上下文。

相反,如果我们想让 spring 在这两个类之间共享应用程序上下文,它们必须如下所示:

@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...
}
Run Code Online (Sandbox Code Playgroud)

如果你像这样连接你的类,spring只会为类A或B加载一次应用程序上下文,具体取决于测试套件中首先运行的两个类中的哪个类。这可以在多个测试类中复制,唯一的标准是您不应该以不同的方式自定义测试类。任何导致测试类与其他类不同(在 spring 看来)的定制最终都会由 spring 创建另一个应用程序上下文。