跨junit测试类重用spring应用程序上下文
我们有一堆JUnit测试用例(集成测试),它们在逻辑上分为不同的测试类 我们能够为每个测试类加载一次Spring应用程序上下文,并将其重新用于JUnit测试类中的所有测试用例,如中所述 然而,我们只是想知道是否有一种方法可以只为一堆JUnit测试类加载一次Spring应用程序上下文跨junit测试类重用spring应用程序上下文,spring,junit,junit4,spring-test,Spring,Junit,Junit4,Spring Test,我们有一堆JUnit测试用例(集成测试),它们在逻辑上分为不同的测试类 我们能够为每个测试类加载一次Spring应用程序上下文,并将其重新用于JUnit测试类中的所有测试用例,如中所述 然而,我们只是想知道是否有一种方法可以只为一堆JUnit测试类加载一次Spring应用程序上下文 FWIW,我们使用Spring3.0.5、JUnit4.5和Maven来构建项目。是的,这是完全可能的。您只需在测试类中使用相同的位置属性: @ContextConfiguration(locations = "cl
FWIW,我们使用Spring3.0.5、JUnit4.5和Maven来构建项目。是的,这是完全可能的。您只需在测试类中使用相同的
位置
属性:
@ContextConfiguration(locations = "classpath:test-context.xml")
Spring通过位置
属性缓存应用程序上下文,因此如果相同的位置
第二次出现,Spring将使用相同的上下文,而不是创建新的上下文
我写了一篇关于此功能的文章:。Spring文档中也详细描述了它:
这有一个有趣的含义。因为Spring不知道JUnit何时完成,所以它永远缓存所有上下文,并使用JVM关闭钩子关闭它们。这种行为(特别是当您有许多测试类具有不同的位置时)可能会导致内存过度使用、内存泄漏等。缓存上下文的另一个优点。要添加到,从Spring 3.2.2开始@ContextHierarchy
注释可用于具有单独的、关联的多上下文结构。当多个测试类希望共享(例如)内存中的数据库设置(datasource、EntityManagerFactory、tx manager等)时,这非常有用
例如:
@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可以注入到单个单元测试的上下文中
有关手册的更多信息:(搜索“”)基本上,如果您在不同的测试类中具有相同的应用程序上下文配置,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...
}
@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...
}
在这个示例中,类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;
//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...
}
@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创建另一个应用程序上下文。创建您的配置类,如下所示
@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
}
}
值得注意的一点是,如果我们使用@SpringBootTests,但在不同的测试类中再次使用@MockBean,Spring将无法为所有测试重用其应用程序上下文
解决方案是将所有@MockBean移动到一个公共抽象类中
,从而解决问题
@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() {
}
}
啊!!我不知道。我们遵循这种方法已经很长时间了,我(错误地)将测试执行的长时间归因于每个测试类的spring上下文加载。我现在会仔细检查的。谢谢。我宁愿说,spring不知道测试用例的执行顺序。因此,它无法判断以后是否需要上下文,或者是否可以处理上下文。我不明白这到底是怎么回事。每次我执行Run As/JUnit测试时,Eclipse/JUnit都会花2分钟启动环境。如果缓存了任何内容,则不会发生这种情况。是否可以完全通过注释而不是使用XML进行上下文定义?我在文档和这里搜索了很多关于它的内容,但是没有找到任何让我认为这是不可能的东西。如果你有初始值设定者,这不成立吗?在classI’ve multi-module maven中,我正在为每个测试初始化,我试图避免在服务模块中设置数据库(因为它已经加载了dataaccess模块的测试),它对我不起作用!这对我有用!谢谢需要明确的是,没有@ContextHierarchy注释,spring会为每个测试加载我的db。我正在使用“classes”参数:@ContextConfiguration(classes={JpaConfigTest.class,…你知道这可以完全通过注释而不是使用XML进行上下文定义吗?我在文档和这里搜索了很多关于它的内容,但没有找到任何让我认为这是不可能的东西。@Jean-FrançoisSavard你在搜索中运气好吗(通过注释SW而不是XML)?@javadev我希望这就是您要寻找的。下面所有的答案都很好,但我没有context.xml。我是否注释了我进入遗忘的方式?没有context.xml有什么方法可以做到这一点?您找到解决方案的答案了吗?我也有同样的问题,我想通过注释和Spring Boot来完成这一点。