我们有一堆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来构建项目。
答案 0 :(得分:80)
是的,这是完全可能的。您所要做的就是在测试类中使用相同的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什么时候完成,它会缓存所有上下文永远并使用JVM shutdown hook关闭它们。此行为(特别是当您有许多具有不同locations
的测试类时)可能导致过多的内存使用,内存泄漏等。缓存上下文的另一个优点。
答案 1 :(得分:25)
要添加到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”)
答案 2 :(得分: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模拟了bean C,而类B模拟了beanD。因此,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之前创建另一个应用程序上下文。
答案 3 :(得分: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
}
}
答案 4 :(得分: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() {
}
}