当前位置: 首页 > 知识库问答 >
问题:

如何使用Jenkins并行运行集成测试?

漆雕昊天
2023-03-14

现在我们有一个项目,包含两个工作。1) 是带有单元测试的标准构建。2) 是集成测试。它们是这样工作的:

  1. 构建整个项目,运行单元测试,启动集成测试工作

问题是步骤2)现在需要一个多小时才能运行,我想将集成测试并行化,以便它们花费更少的时间。但我不确定我该怎么做。我的第一个想法是,我可以有两个这样的步骤:

  1. 构建整个项目,运行单元测试,启动集成测试工作
  2. 构建整个项目,将其部署到integration server1,对integration server1运行客户端集成测试
  3. 构建整个项目,将其部署到integration server2,对integration server2运行客户端集成测试

那么,如何在integration server1上运行一半的集成测试,在integration server2上运行另一半的集成测试呢?我使用的是maven,所以我可能会找到故障保护和复杂的包含/排除模式。但这听起来像是需要付出很多努力才能维持的事情。当有人添加一个新的集成测试类时,我如何确保它在两台服务器中的一台上运行?开发人员是否必须修改maven模式?

共有3个答案

谢财
2023-03-14

是的,如果你有2个从机或一个从机和8个执行器,那么并行测试执行器是一个很酷的插件,因为这个插件基于“测试拆分”,所以例如:你将junit测试拆分为4个不同的数组,这些数组将在你指定的从机上的4个不同执行器上运行。我希望你明白了:D,这取决于你想运行并行测试的从机上的执行器数量,或者你应该将分离测试的计数从4减少到2。

邢承弼
2023-03-14

我相信你现在已经找到了解决办法,但是我会为其他人留下一条路径,他们会打开这个页面问同样的问题:
并行测试执行器插件:
“这个插件添加了一个新的生成器,可以让你轻松地并行执行在单独作业中定义的测试。这是通过让Jenkins查看上次运行的测试执行时间,将测试拆分为大小大致相同的多个单元,然后并行执行来实现的。”
https://wiki.jenkins-ci.org/display/JENKINS/Parallel测试执行器插件

寇和璧
2023-03-14

我发现了这篇关于如何做到这一点的伟大文章,但它提供了一种在Groovy代码中实现这一点的方法。我基本上遵循了这些步骤,但我还没有编写按持续时间平均分配测试的代码。但这仍然是一个有用的工具,所以我将与大家分享。

import junit.framework.JUnit4TestAdapter;
import junit.framework.TestSuite;
import org.junit.Ignore;
import org.junit.extensions.cpsuite.ClassesFinder;
import org.junit.extensions.cpsuite.ClasspathFinderFactory;
import org.junit.extensions.cpsuite.SuiteType;
import org.junit.runner.RunWith;
import org.junit.runners.AllTests;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

@RunWith(AllTests.class)
public class DistributedIntegrationTestRunner {

    private static Logger log = LoggerFactory.getLogger(DistributedIntegrationTestRunner.class);

    public static TestSuite suite() {
        TestSuite suite = new TestSuite();

        ClassesFinder classesFinder = new ClasspathFinderFactory().create(true,
                new String[]{".*IntegrationTest.*"},
                new SuiteType[]{SuiteType.TEST_CLASSES},
                new Class[]{Object.class},
                new Class[]{},
                "java.class.path");

        int nodeNumber = systemPropertyInteger("node.number", "0");
        int totalNodes = systemPropertyInteger("total.nodes", "1");

        List<Class<?>> allTestsSorted = getAllTestsSorted(classesFinder);
        allTestsSorted = filterIgnoredTests(allTestsSorted);
        List<Class<?>> myTests = getMyTests(allTestsSorted, nodeNumber, totalNodes);
        log.info("There are " + allTestsSorted.size() + " tests to choose from and I'm going to run " + myTests.size() + " of them.");
        for (Class<?> myTest : myTests) {
            log.info("I will run " + myTest.getName());
            suite.addTest(new JUnit4TestAdapter(myTest));
        }

        return suite;
    }

    private static int systemPropertyInteger(String propertyKey, String defaultValue) {
        String slaveNumberString = System.getProperty(propertyKey, defaultValue);
        return Integer.parseInt(slaveNumberString);
    }

    private static List<Class<?>> filterIgnoredTests(List<Class<?>> allTestsSorted) {
        ArrayList<Class<?>> filteredTests = new ArrayList<Class<?>>();
        for (Class<?> aTest : allTestsSorted) {
            if (aTest.getAnnotation(Ignore.class) == null) {
                filteredTests.add(aTest);
            }
        }
        return filteredTests;
    }

    /*
    TODO: make this algorithm less naive.  Sort each test by run duration as described here: http://blog.tradeshift.com/just-add-servers/
     */
    private static List<Class<?>> getAllTestsSorted(ClassesFinder classesFinder) {
        List<Class<?>> allTests = classesFinder.find();
        Collections.sort(allTests, new Comparator<Class<?>>() {
            @Override
            public int compare(Class<?> o1, Class<?> o2) {
                return o1.getSimpleName().compareTo(o2.getSimpleName());
            }
        });
        return allTests;
    }

    private static List<Class<?>> getMyTests(List<Class<?>> allTests, int nodeNumber, int totalNodes) {
        List<Class<?>> myTests = new ArrayList<Class<?>>();

        for (int i = 0; i < allTests.size(); i++) {
            Class<?> thisTest = allTests.get(i);
            if (i % totalNodes == nodeNumber) {
                myTests.add(thisTest);
            }
        }

        return myTests;
    }
}

ClasspathFinderFactory用于查找与匹配的所有测试类*集成测试模式。

我做了N个作业,它们都运行这个Runner,但是它们都对node.number系统属性使用不同的值,所以每个作业运行一组不同的测试。这是故障保护插件的样子:

        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-failsafe-plugin</artifactId>
            <version>2.12.4</version>
            <executions>
                <execution>
                    <id>integration-tests</id>
                    <goals>
                        <goal>integration-test</goal>
                        <goal>verify</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <includes>
                    <include>**/DistributedIntegrationTestRunner.java</include>
                </includes>
                <skipITs>${skipITs}</skipITs>
            </configuration>
        </plugin>

ClasspathFinderFactory来自

        <dependency>
            <groupId>cpsuite</groupId>
            <artifactId>cpsuite</artifactId>
            <version>1.2.5</version>
            <scope>test</scope>
        </dependency>

我想应该有一些Jenkins插件,但我还没找到。比较接近的是并行测试执行器,但我不认为这和我需要的一样。它似乎在一个作业/服务器上运行所有测试,而不是在多个服务器上运行。它没有提供一种明显的方式来表示“在这里运行这些测试,在那里运行这些测试”。

 类似资料:
  • 我有几个繁重的Spring集成测试(是的,这不是最好的方法,我没有时间正确地模拟所有外部dep) 下面是测试的典型注释 由于以下原因,测试会定期失败: 这里有两个问题:1、让测试共存的正确方式是什么?我在surefire插件中设置了forkCount=0。好像有帮助 2.1. 在每次测试期间,我实际上不需要启动所有的

  • 问题内容: 我在运行Ubuntu的本地计算机上设置Jenkins,将其指向我的jdk,然后进行maven,创建了一个作业来运行我的Selenium测试,并为它提供了项目中pom.xml的路径,但是当我尝试运行该作业时,它立即失败。控制台输出显示 在工作区中构建/ var / lib / jenkins / workspace / new job [new job] $ / usr / share

  • 我将和作为测试依赖项: 我的集成测试命名正确(在、或之后,默认情况下由Failsafe包含,默认情况下由Surefire排除)。 有什么方法可以使用JUnit5测试和Failsafe吗?

  • Spock规范如下所示: 是一个测试记录列表,对于该列表的每个成员,方法将按顺序运行。我想要的是,列表的前k个成员运行一个线程,下面的k个成员在第二个线程中运行,等等...理论上,使用Executors框架和类(因为Spock依赖于JUnit),这可以相对容易地完成。 然而,问题是如何将更小的注入到具有方法的类中,因为Spock不允许构造函数,而JUnitCore需要类名(具有测试/规范的类),而

  • 我试图使用Gradle的标志运行单个集成测试。我添加了另一个源代码集,并将测试放入其中。我有一个集成测试任务 这运行得很好,但是如果我尝试运行一个测试,它会告诉我它找不到匹配的测试。我不想每次编写一个新的集成测试时都必须运行每个集成测试。有办法做到这一点吗?