跨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

我们有一堆JUnit测试用例(集成测试),它们在逻辑上分为不同的测试类

我们能够为每个测试类加载一次Spring应用程序上下文,并将其重新用于JUnit测试类中的所有测试用例,如中所述

然而,我们只是想知道是否有一种方法可以只为一堆JUnit测试类加载一次Spring应用程序上下文


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来完成这一点。