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

时间:2011-12-14 09:17:30

标签: spring junit junit4 spring-test

我们有一堆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来构建项目。

5 个答案:

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

}