Jenins插件修改-TestLink
来源:互联网 发布:手机淘宝流量的来源 编辑:程序博客网 时间:2024/06/14 11:57
问题:
集成完了TestLink和Jenkins,结果更新到TestLink测试build,但是发现如果测试用例失败了,failed detail info在testlink里面看不到。插件提供了插入Junit测试附件,但每次看错误的测试用例都要打开附件也影响工作效率。我希望能再Notes看到具体错误消息。但是插件只提供了如下信息:
只告诉Junit用例的状态和Class。没把具体的错误写出来。
期望:
在Notes里面,Jenkins能把错误的信息也写入里面。比如加入一行message。这样每次查看错误用例就可以知道为什么出错了。直接去测试代码里面定位问题。
解决:
Jenkins强大插件库和开源代码,让我可以去定制自己的需求。我查了下Jenkins的Jira,发现有人和我有一样的问题,并且报了Bug,这个Bug是2013/4/15号报的,至今还是reopen,因为这个bug的优先级只有Minor,如果你想看这个具体的问题点击这里https://issues.jenkins-ci.org/i#browse/JENKINS-17608
言归正传,还是得靠自己,下面大概介绍下,解决问题的思路:
(关于Jenkins插件开发,官网已经很详细。我主要还是解决TestLink具体问题。有兴趣的可以自己去看官网)
Pull源码:
从github上面pull插件testlink的源码。
配置环境:
Jenkins plugin开发环境,我是Eclipse+maven+JDK.具体配置可以参考官网。
编译依赖:
用mvn命令去下载插件的依赖库,我是导入到eclipse,所以直接用mvn eclipse:eclipse
开始看码:
开始之前,大概给大家介绍下Action。
Action是插件用来在Job或Build页面增加功能的一种主要方式,是Jenkins最常用的一个扩展点。从下图中可以看出什么是Action,就是页面左边菜单栏的一个菜单项,还可以在右边的主页面显示相应的功能。
每个继承了Action这个扩展点的插件都要实现3个方法,方法如下:
public interface Action extends hudson.model.ModelObject { java.lang.String getIconFileName(); java.lang.String getDisplayName(); java.lang.String getUrlName();}
第一个方法是菜单项图片,第二个方法是菜单名称,第三方法个是菜单链接。
Action分瞬时和持久2种,这里主要介绍的是瞬时的Action。瞬时的Action可以随时废弃,让另外一个新的Action来取代,适合一些每次构建都要执行操作的插件,但不适合需要保存持久数据的插件。
在Jenkins官网的插件开发指南中,推荐使用Transient***ActionFactory系列的继承点,有TransientViewActionFactory,TransientProjectActionFactory,TransientBuildActionFactory等,使用该系列的继承点,只需要简单的覆写父类的 createFor方法,就可以实现创建瞬时Action的目的,可以根据不同的需要创建Job,Build,View的Action。
打开TestLink源码,先看TestLinkBuilder.java,找到@Extension,jenkins插件开发是提供了一个Extension契约模块,可以是接口或者是抽象类。所以我们要扩展Jenkins就必须实现这些接口或者抽象类的方法。
/** * The Descriptor of this Builder. It contains the TestLink installation.*/@Extensionpublic static final TestLinkBuilderDescriptor DESCRIPTOR = new TestLinkBuilderDescriptor();
进入TestLinkBuilderDescriptor :
public class TestLinkBuilderDescriptor extends BuildStepDescriptor<Builder>
这里BuildStepDescriptor就是一个抽象类,它继承了Descriptor
public abstract class BuildStepDescriptor<T extends BuildStep & Describable<T>> extends Descriptor<T>
这段代码是告诉我TestLink如何实现Extension,这里面我看见下面的定义。所以当我们安装好TestLink插件后会看见这个选项,名字是一致的。
这里就是对应下面的“Invoke TestLink”
private static final String DISPLAY_NAME = <span style="color:#000000;BACKGROUND-COLOR: #ffff99">"Invoke TestLink"; </span>@Override public String getDisplayName() { return DISPLAY_NAME; }
打开TestLinkBuilder.java,继承了Builder,重写了perform
@Overridepublic boolean perform(AbstractBuild<?, ?> build, Launcher launcher,BuildListener listener) throws InterruptedException, IOException {LOGGER.log(Level.INFO, "TestLink builder started");this.failure = false;// TestLink installationlistener.getLogger().println(Messages.TestLinkBuilder_PreparingTLAPI());final TestLinkInstallation installation = DESCRIPTOR.getInstallationByTestLinkName(this.testLinkName);if (installation == null) {throw new AbortException(Messages.TestLinkBuilder_InvalidTLAPI());}TestLinkHelper.setTestLinkJavaAPIProperties(installation.getTestLinkJavaAPIProperties(), listener);final TestLinkSite testLinkSite;final TestCaseWrapper[] automatedTestCases;final String testLinkUrl = installation.getUrl();final String testLinkDevKey = installation.getDevKey();listener.getLogger().println(Messages.TestLinkBuilder_UsedTLURL(testLinkUrl));try {final String testProjectName = expandVariable(build.getBuildVariableResolver(),build.getEnvironment(listener), getTestProjectName());final String testPlanName = expandVariable(build.getBuildVariableResolver(),build.getEnvironment(listener), getTestPlanName());final String platformName = expandVariable(build.getBuildVariableResolver(),build.getEnvironment(listener), getPlatformName());final String buildName = expandVariable(build.getBuildVariableResolver(),build.getEnvironment(listener), getBuildName());final String buildNotes = Messages.TestLinkBuilder_Build_Notes();if(LOGGER.isLoggable(Level.FINE)) {LOGGER.log(Level.FINE, "TestLink project name: ["+testProjectName+"]");LOGGER.log(Level.FINE, "TestLink plan name: ["+testPlanName+"]");LOGGER.log(Level.FINE, "TestLink platform name: ["+platformName+"]");LOGGER.log(Level.FINE, "TestLink build name: ["+buildName+"]");LOGGER.log(Level.FINE, "TestLink build notes: ["+buildNotes+"]");}// TestLink Site objecttestLinkSite = this.getTestLinkSite(testLinkUrl, testLinkDevKey, testProjectName, testPlanName, platformName, buildName, buildNotes);if (StringUtils.isNotBlank(platformName) && testLinkSite.getPlatform() == null) listener.getLogger().println(Messages.TestLinkBuilder_PlatformNotFound(platformName));final String[] customFieldsNames = this.createArrayOfCustomFieldsNames(build.getBuildVariableResolver(), build.getEnvironment(listener));// Array of automated test casesTestCase[] testCases = testLinkSite.getAutomatedTestCases(customFieldsNames);// Transforms test cases into test case wrappersautomatedTestCases = this.transform(testCases);testCases = null;listener.getLogger().println(Messages.TestLinkBuilder_ShowFoundAutomatedTestCases(automatedTestCases.length));// Sorts test cases by each execution order (this info comes from// TestLink)listener.getLogger().println(Messages.TestLinkBuilder_SortingTestCases());Arrays.sort(automatedTestCases, this.executionOrderComparator);} catch (MalformedURLException mue) {mue.printStackTrace(listener.fatalError(mue.getMessage()));throw new AbortException(Messages.TestLinkBuilder_InvalidTLURL(testLinkUrl));} catch (TestLinkAPIException e) {e.printStackTrace(listener.fatalError(e.getMessage()));throw new AbortException(Messages.TestLinkBuilder_TestLinkCommunicationError());}for(TestCaseWrapper tcw : automatedTestCases) { testLinkSite.getReport().addTestCase(tcw); if(LOGGER.isLoggable(Level.FINE)) { LOGGER.log(Level.FINE, "TestLink automated test case ID [" + tcw.getId() + "], name [" +tcw.getName()+ "]"); }}listener.getLogger().println(Messages.TestLinkBuilder_ExecutingSingleBuildSteps());this.executeSingleBuildSteps(automatedTestCases.length, testLinkSite, build, launcher, listener);listener.getLogger().println(Messages.TestLinkBuilder_ExecutingIterativeBuildSteps());this.executeIterativeBuildSteps(automatedTestCases, testLinkSite, build, launcher, listener);// Here we search for test results. The return if a wrapped Test Case// that// contains attachments, platform and notes.try {listener.getLogger().println(Messages.Results_LookingForTestResults());if(getResultSeekers() != null) {for (ResultSeeker resultSeeker : getResultSeekers()) {LOGGER.log(Level.INFO, "Seeking test results. Using: " + resultSeeker.getDescriptor().getDisplayName());resultSeeker.seek(automatedTestCases, build, launcher, listener, testLinkSite);}}} catch (ResultSeekerException trse) {trse.printStackTrace(listener.fatalError(trse.getMessage()));throw new AbortException(Messages.Results_ErrorToLookForTestResults(trse.getMessage()));} catch (TestLinkAPIException tlae) {tlae.printStackTrace(listener.fatalError(tlae.getMessage()));throw new AbortException(Messages.TestLinkBuilder_FailedToUpdateTL(tlae.getMessage()));}// This report is used to generate the graphs and to store the list of// test cases with each found status.final Report report = testLinkSite.getReport();report.tally();listener.getLogger().println(Messages.TestLinkBuilder_ShowFoundTestResults(report.getTestsTotal()));final TestLinkResult result = new TestLinkResult(report, build);final TestLinkBuildAction buildAction = new TestLinkBuildAction(build, result);build.addAction(buildAction);if(report.getTestsTotal() <= 0 && this.getFailIfNoResults() == Boolean.TRUE) {listener.getLogger().println("No test results found. Setting the build result as FAILURE.");build.setResult(Result.FAILURE);} else if (report.getFailed() > 0) {if (this.failedTestsMarkBuildAsFailure != null && this.failedTestsMarkBuildAsFailure) { listener.getLogger().println("There are failed tests, setting the build result as FAILURE.");build.setResult(Result.FAILURE);} else { listener.getLogger().println("There are failed tests, setting the build result as UNSTABLE.");build.setResult(Result.UNSTABLE);}} else if (this.getFailOnNotRun() != null && this.getFailOnNotRun() && report.getNotRun() > 0) { listener.getLogger().println("There are not run tests, setting the build result as FAILURE."); build.setResult(Result.FAILURE);}LOGGER.log(Level.INFO, "TestLink builder finished");// endreturn Boolean.TRUE;}
关于Jenkins代码具体开发我也没有过多研究,毕竟这次只要想解决Notes的message显示问题,毕竟一切以工作为目的。
找到JUnitMethodNameResultSeeker..java。因为我在Jenkins里面配置TestLink更新测试结果是采用“method方式策略”,所以看下找个代码.
如何勾选了Notes,那么执行下面代码
if(this.isIncludeNotes()) {final String notes = this.getJUnitNotes(caseResult);automatedTestCase.appendNotes(notes);}
继续跟踪,Notes的消息就是从整个方法传过去的。
/** * Retrieves the Notes about the JUnit test. * * @param testCase JUnit test. * @return Notes about the JUnit test. */private String getJUnitNotes( CaseResult testCase ){StringBuilder notes = new StringBuilder();notes.append( Messages.Results_JUnit_NotesForTestMethod(testCase.getName(), testCase.getClassName(), testCase.getSkipCount(), testCase.getFailCount(), (testCase.getSuiteResult() != null ? testCase.getSuiteResult().getTimestamp() : null),<span style="BACKGROUND-COLOR: #ffff99">testCase.getErrorDetails())</span>);return notes.toString();}
那么来看看testCase这个类,查看API,发现还有个getErrorDetails方法,正好是我需要的。那么我只要把它传下去。黄色那行,是我添加的。
继续深入,进入Results_JUnit_NotesForTestMethod
/** * name: {0} * classname: {1} * errors: {2} * failures: {3} * time: {4} * * */ public static String Results_JUnit_NotesForTestMethod(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5) { return holder.format("Results.JUnit.NotesForTestMethod", arg1, arg2, arg3, arg4, arg5); }
到这里我想大家都知道了。于是我就重载了这个方法:
//Over load it /** * * name: {0} * classname: {1} * errors: {2} * failures: {3} * time: {4} * message: {5} */ public static String Results_JUnit_NotesForTestMethod(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6) { return holder.format("Results.JUnit.NotesForTestMethod", arg1, arg2, arg3, arg4, arg5, arg6); }
下面的问题就是找到资源文件,这个源码是支持国际化的,我的环境是en,所以找到util/Messages_en.properties
修改成:
Results.JUnit.NotesForTestMethod=name: {0}\nclassname: {1}\nerrors: {2}\nfailures: {3}\ntime: {4}\nmessage: {5}\n
红色部分是我新加的error message
测试
新改好的TestLink,打包重新安装插件。
上图:
看看,message出来了,Junit熟悉的错误消息。再也不用去打开附件或者去Jenkins上查看具体错误了。
- Jenins插件修改-TestLink
- Testlink中文问题修改
- testlink修改邮件配置
- testlink修改默认端口
- Testlink上传文件上限修改
- Testlink
- testlink
- Testlink
- testlink
- testlink
- testlink
- Testlink
- 修改testlink使其适应大数据量
- jenins用户手册-11-管理之工具
- 图文讲解:搭建xampp + testlink +bugfree 测试管理环境及修改xampp 的Apache、MySQL端口
- 关于TestLink
- TestLink 用户手册
- TestLink Configuration
- cannot open file "mfc42u.lib"
- Linux的iostat命令详解
- iOS自定义控件
- log4j.xml配置文件中引入变量
- DR2总线时序的理解(Post CAS AL 延迟)
- Jenins插件修改-TestLink
- 杭电acm1219
- hdu1403Longest Common Substring
- unity3d实现插件快速开发对话系统
- Ubuntu下Eclipse集成Android ADT后logcat只显示Level一列
- [Leetcode]Merge k Sorted Lists
- Java反射详解
- c/c++ 实现split函数
- xcode7.0-iOS9新特性之二:网络适配