2013-06-26 43 views
5

Ngay bây giờ chúng tôi đã có một dự án xây dựng trong hai công việc. 1) Là tiêu chuẩn xây dựng với các bài kiểm tra đơn vị. 2) là các bài kiểm tra tích hợp. Họ làm việc như thế này:Làm cách nào để sử dụng Jenkins để chạy thử nghiệm tích hợp của tôi song song?

  1. xây dựng toàn bộ dự án, kiểm tra đơn vị chạy, hãy bắt đầu thử nghiệm tích hợp công việc
  2. xây dựng toàn bộ dự án, triển khai nó đến máy chủ tích hợp, kiểm tra tích hợp phía khách hàng chạy chống lại máy chủ tích hợp

Vấn đề là bước 2) bây giờ mất hơn một giờ để chạy và tôi muốn song song các bài kiểm tra tích hợp để chúng mất ít thời gian hơn. Nhưng tôi không chắc chắn làm thế nào tôi có thể/nên làm điều này. Suy nghĩ đầu tiên của tôi là tôi có thể có hai bước 2) s như thế này:

  1. việc xây dựng toàn bộ dự án, kiểm tra đơn vị chạy, hãy bắt đầu thử nghiệm tích hợp
  2. xây dựng toàn bộ dự án, triển khai nó vào server1 hội nhập, chạy client kiểm tra tích hợp bên chống lại hội nhập server1
  3. xây dựng toàn bộ dự án, triển khai nó đến hội nhập server2, chạy client kiểm tra tích hợp bên chống lại hội nhập server2

Nhưng sau đó, làm cách nào để chạy một nửa thử nghiệm tích hợp trên máy chủ tích hợp1 và nửa còn lại trên máy chủ tích hợp2? Tôi đang sử dụng maven, vì vậy tôi có thể có thể tìm ra một cái gì đó với failsafe và một phức tạp bao gồm/loại trừ mô hình. Nhưng điều đó nghe có vẻ giống như một cái gì đó mà sẽ mất rất nhiều nỗ lực để duy trì. EG: khi ai đó thêm một lớp thử nghiệm tích hợp mới, làm thế nào để đảm bảo rằng nó được chạy trên một trong hai máy chủ? Nhà phát triển có phải sửa đổi các mẫu maven không?

Trả lời

2

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 ở đó".

+0

> 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. –

1

Tôi tin rằng bạn đã tìm thấy một giải pháp bây giờ, nhưng tôi sẽ để lại một con đường cho những người khác sẽ mở trang này hỏi cùng một câu hỏi:
Parallel kiểm tra thi hành di chúc plugin:
"Plugin này cho biết thêm một mới Điều này đạt được bằng cách để Jenkins xem xét thời gian thực hiện kiểm tra của lần chạy cuối cùng, chia các phép thử thành nhiều đơn vị có kích thước bằng nhau, sau đó thực thi chúng song song. "
https://wiki.jenkins-ci.org/display/JENKINS/Parallel+Test+Executor+Plugin

+0

Nó sẽ là tốt nhất nếu bạn cũng có thể bao gồm các giải pháp thực tế trong câu trả lời của bạn trong trường hợp liên kết này bị hỏng trong tương lai. – Christina

0

Vâng, Parallel thử nghiệm Executor là một plugin tuyệt vời nếu bạn đã có 2 nô lệ hay một nô lệ với 8 người thi hành vì plugin này dựa trên "các bài kiểm tra tách" để ví dụ: bạn chia thử nghiệm junit của bạn thành 4 khác nhau mảng, các mảng này sẽ chạy trên 4 trình xử lý khác nhau trên slave đó những gì bạn đã chỉ định. Tôi hy vọng bạn có được nó: D, nó phụ thuộc vào số lượng người thi hành nô lệ mà bạn muốn chạy thử nghiệm song song hoặc bạn nên giảm các bài kiểm tra chia thành 2 từ 4.

Các vấn đề liên quan