最近一個需求,在測試的時候需要獲取代碼覆蓋率,通過jenkins目前無法解決,查閱jacoco官網覺得可以通過java agent的方式來處理這個問題。
1、下載jacoco的工具類
下載地址 http://www.jacoco.org/jacoco/index.html,直接下載最新的即可。
2、將agent部署到服務器
講下的包中的/lib/jacocoagent拷貝到對應的服務器上,並修改tomcat 目錄下的/bin/catalina.sh文件,在最后增加
JAVA_OPTS="$JAVA_OPTS -javaagent:/yourdir/jacocoagent.jar=includes=yourpackage.*,output=tcpserver,port=8494,address=192.168.46.122"
具體的參數可以參考官網,下面是官網給出的表格
| Option | Description | Default |
destfile |
Path to the output file for execution data. | jacoco.exec |
append |
If set to true and the execution data file already exists, coverage data is appended to the existing file. If set to false, an existing execution data file will be replaced. |
true |
includes |
A list of class names that should be included in execution analysis. The list entries are separated by a colon (:) and may use wildcard characters (* and ?). Except for performance optimization or technical corner cases this option is normally not required. |
* (all classes) |
excludes |
A list of class names that should be excluded from execution analysis. The list entries are separated by a colon (:) and may use wildcard characters (* and ?). Except for performance optimization or technical corner cases this option is normally not required. |
empty (no excluded classes) |
exclclassloader |
A list of class loader names that should be excluded from execution analysis. The list entries are separated by a colon (:) and may use wildcard characters (* and ?). This option might be required in case of special frameworks that conflict with JaCoCo code instrumentation, in particular class loaders that do not have access to the Java runtime classes. |
sun.reflect.DelegatingClassLoader |
inclbootstrapclasses |
Specifies whether also classes from the bootstrap classloader should be instrumented. Use this feature with caution, it needs heavy includes/excludes tuning. | false |
inclnolocationclasses |
Specifies whether also classes without a source location should be instrumented. Normally such classes are generated at runtime e.g. by mocking frameworks and are therefore excluded by default. | false |
sessionid |
A session identifier that is written with the execution data. Without this parameter a random identifier is created by the agent. | auto-generated |
dumponexit |
If set to true coverage data will be written on VM shutdown. The dump can only be written if either file is specified or the output is tcpserver/tcpclient and a connection is open at the time when the VM terminates. |
true |
output |
Output method to use for writing coverage data. Valid options are:
|
file |
address |
IP address or hostname to bind to when the output method is tcpserver or connect to when the output method istcpclient. In tcpserver mode the value "*" causes the agent to accept connections on any local address. |
loopback interface |
port |
Port to bind to when the output method is tcpserver or connect to when the output method is tcpclient. Intcpserver mode the port must be available, which means that if multiple JaCoCo agents should run on the same machine, different ports have to be specified. |
6300 |
classdumpdir |
Location relative to the working directory where all class files seen by the agent are dumped to. This can be useful for debugging purposes or in case of dynamically created classes for example when scripting engines are used. | no dumps |
jmx |
If set to true the agent exposes functionality via JMX under the name org.jacoco:type=Runtime. Please see the security considerations below. |
false |
3、獲取數據類
import java.io.FileOutputStream; import java.io.IOException; import java.net.InetAddress; import java.net.Socket; import org.jacoco.core.data.ExecutionDataWriter; import org.jacoco.core.runtime.RemoteControlReader; import org.jacoco.core.runtime.RemoteControlWriter; public class ExecutionDataClient { private static final String DESTFILE = "jacoco-client.exec"; private static final String ADDRESS = "192.168.47.18"; private static final int PORT = 8494; /** * Starts the execution data request. * * @param args * @throws IOException */ public static void main(final String[] args) throws IOException { final FileOutputStream localFile = new FileOutputStream(DESTFILE); final ExecutionDataWriter localWriter = new ExecutionDataWriter( localFile); // Open a socket to the coverage agent: final Socket socket = new Socket(InetAddress.getByName(ADDRESS), PORT); final RemoteControlWriter writer = new RemoteControlWriter( socket.getOutputStream()); final RemoteControlReader reader = new RemoteControlReader( socket.getInputStream()); reader.setSessionInfoVisitor(localWriter); reader.setExecutionDataVisitor(localWriter); // Send a dump command and read the response: writer.visitDumpCommand(true, false); reader.read(); socket.close(); localFile.close(); System.out.println("ok"); } private ExecutionDataClient() { } }
4、生成報告類
import java.io.File; import java.io.IOException; import org.jacoco.core.analysis.Analyzer; import org.jacoco.core.analysis.CoverageBuilder; import org.jacoco.core.analysis.IBundleCoverage; import org.jacoco.core.tools.ExecFileLoader; import org.jacoco.report.DirectorySourceFileLocator; import org.jacoco.report.FileMultiReportOutput; import org.jacoco.report.IReportVisitor; import org.jacoco.report.html.HTMLFormatter; public class ReportGenerator { private final String title; private final File executionDataFile; private final File classesDirectory; private final File sourceDirectory; private final File reportDirectory; private ExecFileLoader execFileLoader; /** * Create a new generator based for the given project. * * @param projectDirectory */ public ReportGenerator(final File projectDirectory) { this.title = projectDirectory.getName(); this.executionDataFile = new File(projectDirectory, "jacoco-client.exec"); this.classesDirectory = new File(projectDirectory, "../web-interface/target/classes"); this.sourceDirectory = new File(projectDirectory, "../web-interface/src/main/java"); this.reportDirectory = new File(projectDirectory, "coveragereport"); } /** * Create the report. * * @throws IOException */ public void create() throws IOException { // Read the jacoco.exec file. Multiple data files could be merged // at this point loadExecutionData(); // Run the structure analyzer on a single class folder to build up // the coverage model. The process would be similar if your classes // were in a jar file. Typically you would create a bundle for each // class folder and each jar you want in your report. If you have // more than one bundle you will need to add a grouping node to your // report final IBundleCoverage bundleCoverage = analyzeStructure(); createReport(bundleCoverage); } private void createReport(final IBundleCoverage bundleCoverage) throws IOException { // Create a concrete report visitor based on some supplied // configuration. In this case we use the defaults final HTMLFormatter htmlFormatter = new HTMLFormatter(); final IReportVisitor visitor = htmlFormatter .createVisitor(new FileMultiReportOutput(reportDirectory)); // Initialize the report with all of the execution and session // information. At this point the report doesn't know about the // structure of the report being created visitor.visitInfo(execFileLoader.getSessionInfoStore().getInfos(), execFileLoader.getExecutionDataStore().getContents()); // Populate the report structure with the bundle coverage information. // Call visitGroup if you need groups in your report. visitor.visitBundle(bundleCoverage, new DirectorySourceFileLocator( sourceDirectory, "utf-8", 4)); // Signal end of structure information to allow report to write all // information out visitor.visitEnd(); } private void loadExecutionData() throws IOException { execFileLoader = new ExecFileLoader(); execFileLoader.load(executionDataFile); } private IBundleCoverage analyzeStructure() throws IOException { final CoverageBuilder coverageBuilder = new CoverageBuilder(); final Analyzer analyzer = new Analyzer( execFileLoader.getExecutionDataStore(), coverageBuilder); analyzer.analyzeAll(classesDirectory); return coverageBuilder.getBundle(title); } /** * Starts the report generation process * * @param args * Arguments to the application. This will be the location of the * eclipse projects that will be used to generate reports for * @throws IOException */ public static void main(final String[] args) throws IOException { // for (int i = 0; i < args.length; i++) { // final ReportGenerator generator = new ReportGenerator(new File( // args[i])); // generator.create(); // } final ReportGenerator generator = new ReportGenerator(new File("/Users/tangrubei/projects/web-interface")); generator.create(); } }
5、啟動tomcat,在測試執行完成后,執行前面的兩個類,生成報告

