maven - 如何使用 Jenkins 并行运行我的集成测试?

标签 maven jenkins continuous-integration build-automation

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

  • 构建整个项目,运行单元测试,开始集成测试工作
  • 构建整个项目,将其部署到集成服务器,针对集成服务器运行客户端集成测试

  • 问题是第 2 步)现在需要一个多小时才能运行,我想并行化集成测试,以便它们花费更少的时间。但我不确定我能/应该如何做到这一点。我的第一个想法是我可以有两个这样的步骤 2):
  • 构建整个项目,运行单元测试,开始集成测试工作
  • 构建整个项目,将其部署到 集成服务器 1 ,针对 运行客户端集成测试集成服务器 1
  • 构建整个项目,将其部署到 集成服务器 2 ,针对 运行客户端集成测试集成服务器 2

  • 但是,我如何在集成服务器 1 上运行一半的集成测试,而在集成服务器 2 上运行另一半?我正在使用 maven,所以我可能可以用 failsafe 找出一些东西和复杂的包含/排除模式。但这听起来像是需要付出很多努力才能维护的东西。 EG:当有人添加一个新的集成测试类时,我如何确保它在两台服务器之一上运行?开发人员是否必须修改 Maven 模式?

    最佳答案

    我找到了 this great article关于如何做到这一点,但它提供了一种在 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用于查找与 .*IntegrationTest 匹配的所有测试类图案。

    我做了 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 插件,但我一直找不到。接近的是 Parallel Test Executor ,但我不认为这与我需要的相同。看起来它在单个作业/服务器而不是多个服务器上运行所有测试。它没有提供一种明显的方式来说明“在这里运行这些测试,然后在那里运行那些测试”。

    关于maven - 如何使用 Jenkins 并行运行我的集成测试?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17327727/

    相关文章:

    jenkins - 如何在没有错误的情况下退出任务[Jenkins]?

    Jenkins 多分支管道扫描而不执行

    java - OGraphDatabase ClassNotFoundException OrientDB 1.7.8

    java - Maven 没有拉动我的依赖

    Jenkinsfile 和多个节点

    selenium-webdriver - 带有 Selenium 网络驱动程序的 Bamboo

    docker - 如何设置liferay进行团队开发和部署?

    java - 我可以将来自 Maven 的属性(在 settings.xml 中定义的密码)注入(inject)到我的 Spring 容器中吗?

    maven - 有没有办法为 wro4j jshint maven 插件生成可靠的报告?

    maven - 如何在另一个项目中向Spring Boot Jar添加依赖项?