95

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

如http://static.springsource.org/spring/docs/current/spring-framework-reference中所述,我们能够为每个测试类加载一次 Spring 应用程序上下文,并将其重新用于 JUnit 测试类中的所有测试用例/html/testing.html

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

FWIW,我们使用 Spring 3.0.5、JUnit 4.5 并使用 Maven 构建项目。

4

5 回答 5

105

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

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

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

我写了一篇关于这个特性的文章:加速 Spring 集成测试。Spring 文档中也对其进行了详细描述:9.3.2.1 上下文管理和缓存

这有一个有趣的含义。因为 Spring 不知道 JUnit 何时完成,它会永久缓存所有上下文并使用 JVM 关闭挂钩关闭它们。这种行为(特别是当你有很多不同的测试类时locations)可能会导致过多的内存使用、内存泄漏等。缓存上下文的另一个优点。

于 2011-12-14T09:22:47.680 回答
29

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

于 2014-04-11T05:55:38.710 回答
5

基本上,如果您在不同的测试类中具有相同的应用程序上下文配置,那么 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 类模拟 bean D。因此,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 创建另一个应用程序上下文。

于 2020-03-18T23:21:00.803 回答
4

值得注意的一点是,如果我们再次使用 @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() {
   }

}
于 2020-09-04T17:31:42.653 回答
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 
    }


}
于 2020-04-24T06:15:16.763 回答