【问题标题】:Reuse spring application context across junit test classes跨junit测试类重用spring应用程序上下文
【发布时间】:2012-01-20 01:54:40
【问题描述】:

我们有一堆 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 构建项目。

【问题讨论】:

  • 下面的所有答案都很棒,但我没有 context.xml。我是否已经注释掉了我的遗忘方式?没有 context.xml 有什么方法可以做到这一点?
  • 您找到解决方案的答案了吗?我有同样的问题,我想用注释和 Spring Boot 来完成。

标签: spring junit junit4 spring-test


【解决方案1】:

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

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

Spring 通过locations 属性缓存应用程序上下文,因此如果相同的locations 第二次出现,Spring 将使用相同的上下文而不是创建新的上下文。

我写了一篇关于这个功能的文章:Speeding up Spring integration tests。 Spring文档中也有详细描述:9.3.2.1 Context management and caching

这有一个有趣的含义。因为 Spring 不知道 JUnit 何时完成,它会缓存所有上下文 forever 并使用 JVM 关闭挂钩关闭它们。这种行为(尤其是当您有很多具有不同 locations 的测试类时)可能会导致内存使用过多、内存泄漏等。缓存上下文的另一个优势。

【讨论】:

  • 我宁愿说,spring 不知道你的测试用例的执行顺序。因此,它无法判断上下文是否在以后需要,或者可以被释放。
  • 我不明白这怎么可能是真的。每次我进行 Run As/JUnit 测试时,Eclipse/JUnit 都会花费 2 分钟来启动环境。如果缓存了任何内容,则不会发生这种情况。
  • 知道这是否可以完全通过注释而不是使用 XML 进行上下文定义来完成?我在文档和这里搜索了很多关于它的内容,但找不到任何让我认为这是不可能的东西。
  • @Jean-FrançoisSavard - 使用classes 而不是locations 作为@ContextConfiguration 注释属性
  • 是否可以这样做/结合@SpringBootTest?
【解决方案2】:

要添加到Tomasz Nurkiewicz's answer,从 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”的上下文只会被创建一次,但是里面的bean可以被注入到单个单元测试的上下文中

有关手册的更多信息:http://docs.spring.io/spring/docs/current/spring-framework-reference/html/testing.html#testcontext-ctx-management(搜索“context hierarchy”)

【讨论】:

  • 我有多模块 maven,我尽量避免在服务模块中设置数据库(因为它已经加载了数据访问模块的测试),它对我不起作用!
  • 这对我有用!谢谢。为了清楚起见,没有@ContextHierarchy 注释,spring 会为每个测试加载我的数据库。我正在使用“类”参数:@ContextConfiguration(classes = {JpaConfigTest.class, ...
  • 知道这是否可以完全通过注释而不是使用 XML 进行上下文定义来完成?我在文档和这里搜索了很多关于它的内容,但找不到任何让我认为这是不可能的东西。
  • @Jean-FrançoisSavard 您的搜索是否顺利(通过注释而不是 XML)?
  • @javadev 我希望这就是你要找的东西docs.spring.io/spring/docs/current/spring-framework-reference/…
【解决方案3】:

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

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

【讨论】:

    【解决方案4】:

    值得注意的一点是,如果我们使用@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() {
       }
    
    }
    

    【讨论】:

      【解决方案5】:

      如下所示创建您的配置类

      @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 
          }
      
      
      }
      

      【讨论】:

        猜你喜欢
        • 2013-02-03
        • 2013-01-21
        • 2018-07-11
        • 2015-04-27
        • 2021-06-23
        • 1970-01-01
        • 2013-02-08
        • 1970-01-01
        • 2018-07-22
        相关资源
        最近更新 更多