Spring集成测试使用自动装配时速度较慢

19

我正在尝试加快我们环境中的集成测试速度。所有的类都已经自动装配了。在我们的applicationContext.xml文件中,我们定义了以下内容:

<context:annotation-config/>
<context:component-scan base-package="com.mycompany.framework"/>
<context:component-scan base-package="com.mycompany.service"/>
...additional directories

我注意到Spring正在扫描上面指定的所有目录,然后遍历每个bean并缓存每个bean的属性。(我查看了Spring的DEBUG消息)

因此,以下测试需要大约14秒才能运行:

public class MyTest extends BaseSpringTest {
  @Test
  def void myTest(){
    println "test"
  }
}

有没有一种方法可以懒加载配置呢?我尝试添加default-lazy-init="true"但没有成功。

理想情况下,只需实例化测试所需的bean。

提前感谢。

更新:我之前应该说明这一点,我不想为每个测试都创建一个上下文文件。我也不认为仅为测试创建一个上下文文件会起作用。(测试上下文文件最终将包含所有内容)

7个回答

16

如果你真的想加速你的应用程序上下文,在运行任何测试之前禁用你的<component-scan并执行以下步骤。

Resource resource = new ClassPathResource(<PUT_XML_PATH_RIGHT_HERE>); // source.xml, for instance
InputStream in = resource.getInputStream();

Document document = new SAXReader().read(in);
Element root  = document.getRootElement();

/**
  * remove component-scanning
  */
for ( Iterator i = root.elementIterator(); i.hasNext(); ) {
    Element element = (Element) i.next();

    if(element.getNamespacePrefix().equals("context") && element.getName().equals("component-scan"))
        root.remove(element);
}

in.close();

ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(true);
for (String source: new String[] {"com.mycompany.framework", "com.mycompany.service"}) {
    for (BeanDefinition bd: scanner.findCandidateComponents(source)) {
        root
        .addElement("bean")
        .addAttribute("class", bd.getBeanClassName());
    }
}

//add attribute default-lazy-init = true
root.addAttribute("default-lazy-init","true");

/**
  * creates a new xml file which will be used for testing
  */ 
XMLWriter output = new XMLWriter(new FileWriter(<SET_UP_DESTINATION_RIGHT_HERE>));
output.write(document);
output.close(); 

此外,启用。

由于您需要在运行任何测试之前执行上述常规操作,您可以创建一个抽象类,在其中运行以下内容

设置 Java 系统属性用于测试环境,如下所示

-Doptimized-application-context=false

并且

public abstract class Initializer {

    @BeforeClass
    public static void setUpOptimizedApplicationContextFile() {
        if(System.getProperty("optimized-application-context").equals("false")) {
            // do as shown above

            // and

            System.setProperty("optimized-application-context", "true"); 
        }

    }

}

现在,对于每个测试类,只需扩展Initializer


@mkoryak 并启用 **default-lazy-init="true"**。 - Arthur Ronald
我们发现扫描大约需要4秒钟,创建依赖树需要约10秒钟。这会减少扫描时间、依赖树时间还是两者都减少? - Tihom
@Tihom 你只需要运行一次。没有其他的操作。它会创建它的 XML 对应项并避免反射开销。 - Arthur Ronald

2
一种方法是完全跳过自动检测,而是加载一个单独的上下文(包含测试所需的组件),或在测试运行之前重新定义您的bean。此线程讨论了bean的重新定义和用于此目的的自定义测试类:Spring beans redefinition in unit test environment

+1 表示指出了我不知道的关于 Spring 测试的事情。 - Ither

1

可能你需要重构你的配置文件,使用更少的自动装配。我的方法几乎总是按名称连接bean,尝试用设计明确,但同时也不会太冗长,当明确使用自动装配时,使用自动装配来隐藏细节。

补充: 如果这还不够,并且你正在使用junit,你可能想使用JUnit Addons项目中的一个实用程序。类DirectorySuiteBuilder从目录结构动态构建测试套件。所以你可以做一些像这样的事情

DirectorySuiteBuilder builder = new DirectorySuiteBuilder();
Test suite = builder.suite("project/tests");

在运行此代码之前初始化 Spring 上下文,您可以一次性运行所有测试。但是,如果每个测试都假定具有“干净”的 Spring 上下文,则您可能会遇到问题。


这个项目非常庞大,这样做并不是很可行。我们已经自动装配了大部分内容。 - Tihom

1

这就是使用自动检测组件的代价——速度会变慢。即使您的测试只需要某些 bean,您的 <context:component-scan> 覆盖范围更广,Spring 将实例化和初始化它找到的每个 bean。

我建议您为测试使用不同的 beans 文件,仅定义测试本身所需的 bean,即不使用 <context:component-scan>


因此,这个想法是为测试(s)拥有单独的应用程序上下文(context)...每当我有一个需要使用另一个bean的test1类时,我将其添加到test1上下文中而不使用组件扫描。或者根据包创建测试,这似乎是无意义的。 - lisak
我曾考虑过这个问题,但是我试图避免为每个测试文件定义一个自定义上下文文件。我们正在使用这些测试进行部分集成测试(它们需要访问数据库)。 - Tihom

0
由于这里的所有答案都没有解决我的问题,因此我将添加自己的经验。
我的问题在于Spring、Hibernate和EhCache试图通过详细的DEBUG消息淹没我的控制台,导致日志无法阅读,更糟糕的是性能不佳。
配置它们的日志级别可以解决一切问题:
Logger.getLogger("org.hibernate").setLevel(Level.INFO);
Logger.getLogger("net.sf.ehcache").setLevel(Level.INFO);
Logger.getLogger("org.springframework").setLevel(Level.INFO);

0

0
在这种情况下,你需要找到一个平衡点。 一方面,你想尽快地运行测试,以便快速获得结果,尤其是在团队环境中进行持续集成时更为重要。 另一方面,你也想尽可能简化测试配置,以免测试套件的维护工作变得过于繁琐而无法发挥作用。
但归根结底,你需要找到自己的平衡点并做出决定。 我建议创建一些上下文配置文件来对测试进行分组,这样一些简单的测试就不会花费太长时间仅仅是由Spring进行配置,同时尽量减少配置文件的数量。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接