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


83

我们有一堆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
以下所有答案都很不错,但我没有context.xml。我是否已注释掉遗忘的方式?没有context.xml可以做到这一点吗?
markthegrea

2
您找到解决方案的答案了吗?我有同样的问题,我想通过注解和Spring Boot完成。
AleksandarT

Answers:


96

是的,这完全有可能。您要做的就是locations在测试类中使用相同的属性:

@ContextConfiguration(locations = "classpath:test-context.xml")

Spring按locations属性缓存应用程序上下文,因此,如果locations第二次出现相同的上下文,Spring将使用相同的上下文,而不是创建一个新的上下文。

我写了一篇有关此功能的文章:加速Spring集成测试。在Spring文档中也对其进行了详细描述:9.3.2.1上下文管理和缓存

这具有有趣的含义。因为Spring不知道JUnit何时完成,所以它将永远缓存所有上下文,并使用JVM shutdown挂钩关闭它们。此行为(尤其是当您有许多不同的测试类时locations)可能导致过多的内存使用,内存泄漏等。缓存上下文的另一个优点。


啊! 没意识到。我们长期以来一直采用这种方法,我(错误地)将测试执行的长时间归因于每个测试类的弹簧上下文加载。现在将仔细检查。谢谢。
拉梅什

1
我想说,那个春天不了解您的测试用例的执行顺序。结果,它无法确定以后是否需要上下文或可以将其丢弃。
集邮2015年

1
我不知道这怎么可能是真的。每次我执行Run As / JUnit测试时,Eclipse / JUnit都会花2分钟来启动环境。如果缓存了任何内容,则不会发生这种情况。
user1944491 2015年

3
是否可以通过注释完全完成此操作,而不是使用XML进行上下文定义?我在文档中以及SO上对此进行了很多搜索,但是找不到任何东西使我认为这是不可能的。
让·弗朗索瓦·Savard

如果有初始化程序,这不成立吗?我的班级的每一个测试都在初始化
Kalpesh Soni

26

要添加到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(搜索“上下文层次结构”)


我有多模块专家,并且我尝试避免在服务模块中设置数据库(因为它已经加载了dataaccess模块​​的测试),但它对我不起作用!
Muhammad Hewedy 2014年

5
这对我有用!谢谢。为了清楚起见,没有@ContextHierarchy批注,spring会为每个测试加载我的数据库。我正在使用“类”参数:@ContextConfiguration(classes = {JpaConfigTest.class,...
Brel

5
是否可以通过注释完全完成此操作,而不是使用XML进行上下文定义?我在文档中以及SO上对此进行了很多搜索,但是找不到任何东西使我认为这是不可能的。
让·弗朗索瓦·Savard

1
@Jean-FrançoisSavard您的搜索是否有任何运气(通过annotationsw而不是XML)?
javadev


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之前创建另一个应用程序上下文。


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 
    }


}

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

}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.