Tôi đã tìm thấy this great article về cách thực hiện việc này, nhưng nó cung cấp một cách để thực hiện điều đó trong mã Groovy. Tôi đã làm theo nhiều bước này, nhưng tôi đã không viết mã để phân phối các bài kiểm tra đồng đều theo thời gian. Nhưng đây vẫn là một công cụ hữu ích nên tôi sẽ chia sẻ nó.
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;
}
}
Các ClasspathFinderFactory
được sử dụng để tìm tất cả các lớp học thử nghiệm phù hợp với mô hình .*IntegrationTest
.
Tôi thực hiện N công việc và tất cả đều chạy này Runner
nhưng tất cả đều sử dụng các giá trị khác nhau cho thuộc tính hệ thống node.number
, do đó, mỗi công việc đều chạy một bộ kiểm tra khác. Đây là cách các plugin chạy failsafe trông:
<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>
Các ClasspathFinderFactory
đến từ
<dependency>
<groupId>cpsuite</groupId>
<artifactId>cpsuite</artifactId>
<version>1.2.5</version>
<scope>test</scope>
</dependency>
Tôi nghĩ nên có một số plugin Jenkins cho điều này, nhưng tôi đã không thể tìm thấy một. Một cái gì đó gần gũi là Parallel Test Executor, nhưng tôi không nghĩ rằng điều này không cùng một điều tôi cần. Dường như nó chạy tất cả các bài kiểm tra trên một công việc/máy chủ duy nhất thay vì nhiều máy chủ. Nó không cung cấp một cách rõ ràng để nói, "chạy các bài kiểm tra ở đây, và những bài kiểm tra ở đó".
> Dường như nó chạy tất cả các bài kiểm tra một công việc/máy chủ thay vì nhiều máy chủ. Khi công việc kiểm tra được cấu hình để "Thực thi các bản dựng đồng thời nếu cần thiết", các lần chạy có thể được chạy trên một nút khác của Jenkins. –