Fork me on GitHub
持续集成(CI)- TeamCity实战概览

 

   
TeamCity扩展——构建脚本与TeamCity交互
2011-01-08 23:48

TeamCity中本身自带了一些单元测试插件,如Nunit、Junit、MSTest等,使用TeamCity自带的测试插件执行测试案例,TeamCity服务器会自动收集测试结果,然而那些TeamCity不支持的插件该如果做呢?

我们可以通过修改构建脚本将测试结果进行标准输出,然后TeamCity服务器会捕获标准输出信息,实现测试结果的收集。从而实现及时显示测试结果,我们可以在Build Results页面中看到Tests标签,记录了每个案例的信息。

 If TeamCity doesn't support your testing framework or build runner, it is possible to modify your build script to send this information to the TeamCity server using service messages that are sent via the standard output of the build process. This makes it possible to display test results in real-time, make test information available on the Tests tab of the Build Results page, and publish artifacts while the build is still in progress.

流程:

FlexUnit测试结果输出到xml文件 --> 编写工具实现 解析测试结果并输出到命令行 

Service messages are used to pass commands/build information to TeamCity server from the build script. In order to be processed by TeamCity they should be printed into standard output stream of the build (otherwise, if the output is not in the service message syntax, it should appear in the build log). A single service message should not contain a newline character inside it, it should not span across multiple lines.

以下示例:(解析Nunit测试结果文件xml  使其符合TeamCity的标准输出方式  输出到命令行)

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            string xmlpath = @"D:\TestResult.xml";
            XmlDocument xmldoc = new XmlDocument();
            xmlpath = xmlpath.Trim();
            try
            {
                xmldoc.Load(xmlpath);
            }
            catch (Exception e)
            {
                throw new Exception("内容不符合标准", e);
            }

          //  Nunit XML示例

            //            <test-case name="Class.Class.TCore_Coordinate_Clone_1" executed="True" success="False" time="12.658" asserts="2">
            //                <categories>
            //                  <category name="Coordinate" />
            //                </categories>
            //                <failure>
            //                  <message><![CDATA[System.IndexOutOfRangeException : 在位置 0 处没有任何行。]]></message>
            //                  <stack-trace><![CDATA[在 System.Data.RBTree`1.GetNodeByIndex(Int32 userIndex)
            //在 System.Data.RBTree`1.get_Item(Int32 index)
            //在 System.Data.DataRowCollection.get_Item(Int32 index)
            //在 TestUtility.CaseDB.ImportToOracle.StartImporter(DataRow dataRow) 位置 F:\AAA\TestCode\03_Tools\TestUtility\CaseDB\ImportToOracle.cs:行号 111
            //在 TestUtility.CaseDB.TResult.WriteResult() 位置 F:\AAA\TestCode\03_Tools\Utility\CaseDB\TResult.cs:行号 337
            //在 ClassForSL.ClassForSL.UTRunCase(String caseName, Boolean IsImage, Boolean IsText, Int32 sleepTime) 位置 F:\AAA\TestCode\01_TestUnit\TestSelenium\ClassForSL\ClassForSL.cs:行号 151
            //在 ClassForSL.ClassForSL.TCore_Coordinate_Clone_1() 位置 F:\AAA\TestCode\01_TestUnit\TestSelenium\Class\TestCase\Core\CoordinateTest.cs:行号 80
            //]]></stack-trace>
            //                </failure>
            //              </test-case>

            XmlElement root = xmldoc.DocumentElement;
            XmlNodeList nodelist = root.GetElementsByTagName("test-case");

            foreach (XmlNode item in nodelist)
            {
                //案例名称
                string testname = item.Attributes["name"].Value;
                //案例执行状态 Pass|False
                string status="";
                //案例执行时间
                string duration="";
                //案例是否执行
                string executed="";
                if(item.Attributes["success"]!=null)
                    status = item.Attributes["success"].Value;
                if(item.Attributes["time"]!=null)
                duration = item.Attributes["time"].Value;
                executed = item.Attributes["executed"].Value;

                /**
                 *A single service message should not contain a newline character inside it, it should not span across multiple lines.
                 * For escaped values, TeamCity uses a vertical bar "|" as an escape character. In order to have certain characters properly interpreted by the TeamCity server they must be preceded by a vertical bar.
                 * For example, the following message:
                 * \n (line feed)   |n
                 * \r (carriage return)  |r
                 * EX:
                 * ##teamcity[testStarted name='test1']
                 * ##teamcity[testFailed name='test1' message='failure message' details='message and stack trace']
                 * ##teamcity[testFinished name='test1' duration='50']
                **/

                Console.WriteLine("##teamcity[testStarted name='" + testname + "']");
                if (executed == "False")
                {
                    //忽略案例
                    string resonText = item.SelectSingleNode("child::reason").InnerText.Trim();
                    Console.WriteLine("##teamcity[testIgnored name='"+testname+"' message='"+resonText+"']");                  
                }
                else
                {
                    //失败案例
                    if (status == "False")
                    {
                        XmlNode failNode = item.SelectSingleNode("child::failure");

                        //注意Teamcity中的转义字符   \r == |r   \n == |n
                        string message = failNode.SelectSingleNode("child::message").InnerText.Trim();
                        message = message.Replace("\n", "|n").Replace("\r", "|r ");

                        string details = failNode.SelectSingleNode("child::stack-trace").InnerText.Trim();
                        details = details.Replace("\n", "|n").Replace("\r", "|r ");
                        Console.WriteLine("##teamcity[testFailed name='" + testname + "' message='" + message + "' details='" + details + "']");
                    }
                }

                Console.WriteLine("##teamcity[testFinished name='" + testname + "' duration='"+duration+"']");
            }
        }
    }

注:

TeamCity Service Message中支持的测试案例字段有:

不是单元测试工具中的所有字段全部支持,基本上常用的都支持,如测试案例名称、测试案例是否Ignore、测试案例执行时间、测试案例错误信息等。

如下:

##teamcity[testStarted name='testname']

##teamcity[testFailed name='test1' message='failure message' details='message and stack trace']

##teamcity[testStdOut name='testname' out='text']

##teamcity[testStdErr name='testname' out='text']

##teamcity[testFinished name='testname' duration='50']

##teamcity[testIgnored name='testname' message='ignore comment']

最后在TeamCity中新建一个Build Configuration Step,然后Build Runner中指定为上面的工具即可

最后工具中的输出信息会被TeamCity服务器捕获

之所以选择TeamCity,有以下几个原因:

Ø 这个软件对于小团队可以免费使用

Ø 安装配置比较简单,系统的要求不是很高(相比VS 2010 TFS)

Ø 使用和配置比Cc.net简单一些

Ø 包含了重复代码的检测和分析工具

一、SVN安装

SVN服务安装 http://www.visualsvn.com/files/VisualSVN-Server-2.1.7.msi

clip_image002

SVN客户端

TortoiseSVN

VisualSVN-2.0.5.msi

clip_image004

二、TeamCity安装

http://www.jetbrains.com/teamcity/ 下载 TeamCity-6.0.3.exe

三、TeamCity实战

如下把涉及到的多个方面简要汇总一下,具体的内容可以试用一下,或者从参考中下载图书的代码内容分析。一般的项目也不会全部涉及,根据项目的情况和内容裁减即可。

1、建立CI项目

默认的ci管理网址在 http://localhost/

按照提示建立项目即可

成功后即可建立各种类型的Build

2、NUnit、覆盖率测试

TeamCity comes with built-in functionality for NCover and PartCover. To use PartCover, you have to set it up on the Build Configuration page.

First, enable NUnit Tests (mark the flag in New Unit Test Settings). Set it to run the tests from

%system.teamcity.build.workingDir%\CiDotNet.Calc.Test\bin\Release\CiDotNet.Calc.Test.dll.

Go to the .NET Coverage section, choose PartCover from the drop-down list, and provide the path to the executables in the lib directory

(%system.teamcity.build.workingDir%\lib\PartCover\PartCover.exe).

Report XSLT:

C:\Program Files (x86)\PartCover\PartCover .NET 4.0\xslt\Report By Assembly.xslt=> PartCover.Assembly.html

C:\Program Files (x86)\PartCover\PartCover .NET 4.0\xslt\ Report By Class.xslt=> PartCover.Class.html

PartCover在64位下执行的方法

CorFlags.exe PartCover.exe /32BIT+ /Force

3、MSTest测试MSBuild形式

<Project DefaultTargets="Test" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

<PropertyGroup>

<!--Default Configuration-->

<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>

<!--Default Platform-->

<Platform Condition=" '$(Platform)' == '' ">"Any CPU"</Platform>

<!--Visual Studio path-->

<VSPath Condition=" '$(VSPath)' == '' ">%ProgramFiles(x86)%\Microsoft Visual Studio 10.0\Common7\IDE</VSPath>

<!--Asseblies to test-->

<TestAssemblies>CiDotNet.Calc.Test\bin\$(Configuration)\CiDotNet.Calc.Test.dll</TestAssemblies>

</PropertyGroup>

<Target Name="Build" >

<MSBuild Targets="Clean;Rebuild" Projects="CiDotNet.sln" ContinueOnError="false"/>

</Target>

<Target Name="Test" DependsOnTargets="Build">

<Delete Condition="Exists('MSTestReport.trx')" Files="MSTestReport.trx"></Delete>

<Exec Command="&quot;$(VSPath)\MSTest.exe&quot; /testcontainer:$(TestAssemblies) /resultsfile:MSTestReport.trx"/>

<Message Text="##teamcity[importData type='mstest' path='MSTestReport.trx']" ></Message>

</Target>

</Project>

clip_image006

clip_image008

clip_image010

4、MSTest

MSTest.exe 命令行选项

http://msdn.microsoft.com/zh-cn/library/ms182489.aspx

· Build Log

[16:26:40]: 测试设置: 本地

[16:26:42]: ##teamcity[importData id='mstest' file='D:\pm\TeamCity\buildAgent\work\eea58b62b6f4c74d\MSTestReportX.trx']

[16:26:42]: Importing data from 'D:\pm\TeamCity\buildAgent\work\eea58b62b6f4c74d\MSTestReportX.trx' with 'mstest' processor

[16:26:42]: MSTest

[16:26:42]: [MSTest] Found 1 test definitions.

[16:26:42]: [MSTest

找到MSTestReportX.trx文件,VS打开即可看到覆盖率

clip_image012

5、Integration tests

不像单元测试,尽量减少组件之间的关联,依赖的组件使用Mock框架进行模拟

此时引入相关的依赖组件,替换掉Mock

6、其他测试

参考中图书第7章的例子代码拿来用用

涉及:

White框架

CiDotNet.WinCalc.Test: WinForm的测试

CiDotNet.SilverlightCalc.Test: Silverlight的测试

Selenium框架 http://seleniumhq.org/

CiDotNet.WebCalc.Test

8、代码规范

这部分主要是修改项目文件,支持Build,然后提供信息反馈处,具体的设置参考如下

FxCop

使用Fxcop的项目进行集成, *.fxcop 是项目文件

<Target Name="Analyze">

<Delete Condition="Exists('FxCopReport.xml')" Files="FxCopReport.xml"></Delete>

<FileUpdate Files="CiDotNet.FxCop" Regex="bin/Debug" ReplacementText="bin/$(Configuration)" />

<Exec Command="tools\FxCop\FxCopCmd.exe /project:CiDotNet.FxCop /out:FxCopReport.html /axsl"/>

<Error Condition="Exists('FxCopReport.xml')" Text="FxCop found some broken rules!" />

define a new artifact FxCopReport.html and a new tab server configuration.

StyleCop

<Project DefaultTargets="Analyze" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

<UsingTask AssemblyFile="tools\MSBuildCommunityTasks\MSBuild.Community.Tasks.dll" TaskName="MSBuild.Community.Tasks.FileUpdate"></UsingTask>

<UsingTask AssemblyFile="tools\MSBuildCommunityTasks\MSBuild.Community.Tasks.dll" TaskName="MSBuild.Community.Tasks.Xslt"></UsingTask>

<UsingTask AssemblyFile="tools\MSBuildCommunityTasks\MSBuild.Community.Tasks.dll" TaskName="MSBuild.Community.Tasks.XmlRead"></UsingTask>

<UsingTask AssemblyFile="tools\StyleCop\Microsoft.StyleCop.dll" TaskName="StyleCopTask"/>

<CreateItem Include="CiDotNet.Calc\**\*.cs">

<Output TaskParameter="Include" ItemName="StyleCopFiles"/>

</CreateItem>

<StyleCopTask

ProjectFullPath="$(MSBuildProjectFile)"

SourceFiles="@(StyleCopFiles)"

ForceFullAnalysis="true"

TreatErrorsAsWarnings="true"

OutputFile="StyleCopReport.xml"

CacheResults="true"

AdditionalAddinPaths="tools\"

/>

<Xslt Inputs="StyleCopReport.xml"

RootTag="StyleCopViolations"

Xsl="tools\StyleCop\StyleCopReport.xsl"

Output="StyleCopReport.html" />

<XmlRead XPath="count(//Violation)"

XmlFileName="StyleCopReport.xml">

<Output TaskParameter="Value" PropertyName="StyleCopViolations" />

</XmlRead>

<Error Condition="$(StyleCopViolations) > 0" Text="StyleCop found some broken rules!" />

add the artifact file, and a new tab appears in the build report page

代码重复

Its purpose is to find the places in your code where your team has duplicated the code.

The Duplicates Finder is a separate build runner in TeamCity.

clip_image014

9、文档

基于Sandcastle 的形式

<Target Name="Document" >

<MSBuild Projects="CiDotNet.shfbproj"/>

</Target>

TeamCity project’s General Settings, and set the Artifacts path to

Help/**/* => Help

This causes all the Help folder content from the current build to be copied to the Help artifacts folder. To make the documentation visible on the build-report page,add a new tab in the server configuration, pointing to /Help/Index.html.

10、安装和部署

WIX安装

ClickOnce部署

MS Deploy部署

<Target Name="Setup" >

<Exec

Command="&quot;$(VSPath)\devenv.exe&quot; CiDotNet.sln /build &quot;$(Configuration)&quot; /project CiDotNet.WinCalc.Setup\CiDotNet.WinCalc.Setup.vdproj"

ContinueOnError="false" IgnoreExitCode="true"

/>

<MSBuild Projects="CiDotNet.WinCalc.Wix\CiDotNet.WinCalc.Wix.wixproj" Properties="WixTargetsPath=$(MSBuildProjectDirectory)\tools\Wix\Wix2010.targets;"/>

</Target>

<Target Name="Publish" >

<MSBuild Targets="Publish" Projects="CiDotNet.sln" ContinueOnError="false" Properties="ApplicationVersion=$(Major).$(Minor).$(Build).$(Revision)"/>

</Target>

<Target Name="WebPublish" >

<MSBuild Targets="Package" Projects="CiDotNet.WebCalc\CiDotNet.WebCalc.csproj" ContinueOnError="false" Properties="PackageLocation=WebPublication\Package.zip;MSBuildExtensionsPath32=..\tools"/>

<Exec

Command="CiDotNet.WebCalc\WebPublication\Package.deploy.cmd /Y /m:ci1 /u:administrator /p:Marcinq1p0w2"

ContinueOnError="false" IgnoreExitCode="true"

/>

</Target>

11、数据库集成

这个主要使用VS的数据库项目可以完成

<MSBuild

Projects = "$(SolutionRoot)\NWSandbox.dbproj"

Properties = "Configuration=Debug; TargetDatabase=NewDBName"

Targets = "Deploy"/>

四、参考

图书Continuous Integration in .NEThttp://www.manning.com/kawalerowicz/ 下载CIi.N_SourceCode.zip

其中包括VS的Build脚本等,在TFS的持续集成中可以参考

http://www.cnblogs.com/2018/category/300477.html

 
分类: 持续集成(CI)
以前在网上读《走出软件作坊》,对于作者的文章和分析符合国情,很是受用,最近阅读了图书版的内容。作者在blog上又有不少的新更新。
 
作者博客:阿朱=行业趋势+开发管理+架构
http://blog.csdn.net/david_lv/archive/2008/05.aspx
http://blog.csdn.net/david_lv/archive/2008/06.aspx
http://blog.csdn.net/david_lv/archive/2008/07.aspx
http://blog.csdn.net/david_lv/archive/2008/08.aspx
 
如下是一个网站的连载,可以看看,在实施中肯定受用
 
分类: 其它
posted on 2012-07-27 09:22  HackerVirus  阅读(1235)  评论(0编辑  收藏  举报