![](/img/trans.png)
[英]Can Spring Boot test classes reuse application context for faster test run?
[英]Reuse spring application context across junit test classes
我们有一堆 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 构建项目。
是的,这是完全可能的。 你所要做的就是在你的测试类中使用相同的locations
属性:
@ContextConfiguration(locations = "classpath:test-context.xml")
Spring 通过locations
属性缓存应用程序上下文,因此如果第二次出现相同的locations
,Spring 将使用相同的上下文而不是创建新的上下文。
我写了一篇关于这个特性的文章: 加速 Spring 集成测试。 在 Spring 文档中也有详细描述: 9.3.2.1 Context management and caching 。
这有一个有趣的含义。 因为 Spring 不知道 JUnit 何时完成,它永远缓存所有上下文并使用 JVM 关闭钩子关闭它们。 这种行为(特别是当你有很多不同locations
的测试类时)可能会导致内存使用过多、内存泄漏等。缓存上下文的另一个优点。
要添加到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”的上下文将只创建一次,但其中的 bean 可以注入到单个单元测试的上下文中
有关手册的更多信息: http : //docs.spring.io/spring/docs/current/spring-framework-reference/html/testing.html#testcontext-ctx-management (搜索“ 上下文层次结构”)
如果您在不同的测试类中具有相同的应用程序上下文配置,那么基本上 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 创建另一个应用程序上下文。
一个值得注意的点是,如果我们使用 @SpringBootTests 但use @MockBean in different test classes
再次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() {
}
}
创建您的配置类,如下所示
@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
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.