简体   繁体   English

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

[英]Reuse spring application context across junit test classes

We've a bunch of JUnit test cases (Integration tests) and they are logically grouped into different test classes.我们有一堆 JUnit 测试用例(集成测试),它们在逻辑上被分组到不同的测试类中。

We are able to load Spring application context once per test class and re-use it for all test cases in a JUnit test class as mentioned in http://static.springsource.org/spring/docs/current/spring-framework-reference/html/testing.html我们能够为每个测试类加载一次 Spring 应用程序上下文,并将其重新用于 JUnit 测试类中的所有测试用例,如http://static.springsource.org/spring/docs/current/spring-framework-reference 中所述/html/testing.html

However, we were just wondering if there is a way to load Spring application context only once for a bunch of JUnit test classes.然而,我们只是想知道是否有一种方法可以为一堆 JUnit 测试类只加载一次 Spring 应用程序上下文。

FWIW, we use Spring 3.0.5, JUnit 4.5 and use Maven to build the project. FWIW,我们使用 Spring 3.0.5、JUnit 4.5 并使用 Maven 构建项目。

Yes, this is perfectly possible.是的,这是完全可能的。 All you have to do is to use the same locations attribute in your test classes:你所要做的就是在你的测试类中使用相同的locations属性:

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

Spring caches application contexts by locations attribute so if the same locations appears for the second time, Spring uses the same context rather than creating a new one. Spring 通过locations属性缓存应用程序上下文,因此如果第二次出现相同的locations ,Spring 将使用相同的上下文而不是创建新的上下文。

I wrote an article about this feature: Speeding up Spring integration tests .我写了一篇关于这个特性的文章: 加速 Spring 集成测试 Also it is described in details in Spring documentation: 9.3.2.1 Context management and caching .在 Spring 文档中也有详细描述: 9.3.2.1 Context management and caching

This has an interesting implication.这有一个有趣的含义。 Because Spring does not know when JUnit is done, it caches all context forever and closes them using JVM shutdown hook.因为 Spring 不知道 JUnit 何时完成,它永远缓存所有上下文并使用 JVM 关闭钩子关闭它们。 This behavior (especially when you have a lot of test classes with different locations ) might lead to excessive memory usage, memory leaks, etc. Another advantage of caching context.这种行为(特别是当你有很多不同locations的测试类时)可能会导致内存使用过多、内存泄漏等。缓存上下文的另一个优点。

To add to Tomasz Nurkiewicz's answer , as of Spring 3.2.2 @ContextHierarchy annotation can be used to have separate, associated multiple context structure.要添加到Tomasz Nurkiewicz 的答案,从 Spring 3.2.2 开始, @ContextHierarchy注释可用于具有单独的、关联的多上下文结构。 This is helpful when multiple test classes want to share (for example) in-memory database setups (datasource, EntityManagerFactory, tx manager etc).当多个测试类想要共享(例如)内存数据库设置(数据源、EntityManagerFactory、tx 管理器等)时,这很有用。

For example:例如:

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

By having this setup the context that uses "test-db-setup-context.xml" will only be created once, but beans inside it can be injected to individual unit test's context通过进行此设置,使用“test-db-setup-context.xml”的上下文将只创建一次,但其中的 bean 可以注入到单个单元测试的上下文中

More on the manual: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/testing.html#testcontext-ctx-management (search for " context hierarchy ")有关手册的更多信息: http : //docs.spring.io/spring/docs/current/spring-framework-reference/html/testing.html#testcontext-ctx-management (搜索“ 上下文层次结构”)

Basically spring is smart enough to configure this for you if you have the same application context configuration across the different test classes.如果您在不同的测试类中具有相同的应用程序上下文配置,那么基本上 spring 足够聪明,可以为您配置它。 For instance let's say you have two classes A and B as follows:例如,假设您有两个类 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...
}

In this example class A mocks bean C, whereas class B mocks bean D. So, spring considers these as two different configurations and thus would load the application context once for class A and once for class B.在此示例中,类 A 模拟 bean C,而类 B 模拟 bean D。因此,spring 将它们视为两种不同的配置,因此将为类 A 加载应用程序上下文一次,为类 B 加载一次。

If instead, we'd want to have spring share the application context between these two classes, they would have to look something as follows:相反,如果我们希望 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...
}

If you wire up your classes like this, spring would load the application context only once either for class A or B depending on which class among the two is ran first in the test suite.如果你像这样连接你的类,spring 将只为类 A 或 B 加载应用程序上下文一次,具体取决于这两个类中的哪个类在测试套件中首先运行。 This could be replicated across multiple test classes, only criteria is that you should not customize the test classes differently.这可以跨多个测试类复制,唯一的标准是您不应以不同的方式自定义测试类。 Any customization that results in the test class to be different from the other(in the eyes of spring) would end up creating another application context by spring.任何导致测试类与其他类不同的定制(在 spring 眼中)最终都会由 spring 创建另一个应用程序上下文。

One remarkable point is that if we use @SpringBootTests but again use @MockBean in different test classes , Spring has no way to reuse its application context for all tests.一个值得注意的点是,如果我们使用 @SpringBootTests 但use @MockBean in different test classes再次use @MockBean in different test classes ,则 Spring 无法为所有测试重用其应用程序上下文。

Solution is to move all @MockBean into an common abstract class and that fix the issue.解决方案是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;

}

Then the test classes can be seen as below然后可以看到测试类如下

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

}

create your configuaration class like below创建您的配置类,如下所示

@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);


}

Create your test classes like below创建您的测试类,如下所示

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 
    }


}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Spring Boot 测试类可以重用应用程序上下文以加快测试运行速度吗? - Can Spring Boot test classes reuse application context for faster test run? Spring JUnit 测试未加载完整的应用程序上下文 - Spring JUnit Test not loading full Application Context 在 JUnit 测试类中使用属性值而不加载整个 Spring 引导应用程序上下文 - Using property values in JUnit test classes without load whole Spring Boot application context 在不同的Spock测试中重用Spring应用程序上下文 - Reuse Spring application context across different Spock tests 在JUnit测试中将Maven属性传递给Spring Application上下文文件 - Passing Maven properties to Spring Application context files in JUnit test 使用2个单独的spring应用程序上下文运行Junit测试用例 - Running Junit test cases using 2 separate spring Application Context Spring Test / JUnit问题-无法加载应用程序上下文 - Spring Test / JUnit problem - unable to load application context JUnit测试启动Jetty,Jetty启动Spring,可以测试查看spring应用程序上下文 - JUnit test starts Jetty, Jetty starts Spring, can test see spring application context 在另一个项目中重用 Spring 测试上下文 - Reuse a Spring test context in another project 想要使用共享的@ BeforeClass / @ AfterClass和Spring App Context运行许多jUnit测试类 - Want to run many jUnit test classes with a shared @BeforeClass/@AfterClass and Spring App Context
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM