最近做的项目是.NET的,准确来说,是一个ASP.NET的项目,项目要求采用NUnit测试,在每日的版本构建的时候,需要自动运行全体的测试任务。在Java中,用的最多的每日构建工具应该就是Ant了,当然,Maven也是个好东西。在.NET有个类似的工具,叫做NAnt,不过好像已经有1年左右没有更新了,不知道是不是这个NAnt没人维护了呢。
首先在SourceForge上下载了NAnt的安装包,还有,Nant-Contrib的也需要下载,否则很多扩展功能不能自动实现。把Nant解压缩,并且把Contrib的东西也一起解压缩在Nant的bin目录下。然后你再把Nant的bin目录添加到你的环境变量的Path下,这样就可以在命令行中运行nant命令了。
如果要进行NUnit测试,首先需要编译NUnit的测试代码。采用csc命令进行编译,如下所示
<
csc
target
="library"
output
="${nunit_dll}/Test.Isid.ResonaIB.Web.dll"
optimize
="true"
failonerror
="true"
rebuild
="true"
verbose
="${debug}"
>
<
sources
>
<
include
name
="${src.path} /trunk/Test.Isid.ResonaIB.Web/Test.Isid.ResonaIB.Web/**/*.cs"
/>
</
sources
>
<
references
>
<
include
name
="System.dll"
/>
<
include
name
="System.Data.dll"
/>
<
include
name
="System.Xml.dll"
/>
<
include
name
="${nunit_lib}/nunit.framework.dll"
/>
<
include
name
="${nunit_lib}/NMock2.dll"
/>
<
include
name
="${lib}/Isid.Copera.Web.FC.dll"
/>
<
include
name
="${lib}/Isid.Copera.Web.Parameters.dll"
/>
<
include
name
="${lib}/Isid.Dance.Web.FC.dll"
/>
<
include
name
="${lib}/Isid.Dance.Web.Parameters.dll"
/>
<
include
name
="${lib}/Isid.ResonaIB.Web.BC.Certification.dll"
/>
<
include
name
="${lib}/Isid.ResonaIB.Web.BC.External.dll"
/>
<
include
name
="${lib}/Isid.ResonaIB.Web.BC.Resource.dll"
/>
<
include
name
="${lib}/Isid.ResonaIB.Web.BC.SystemCommon.dll"
/>
<
include
name
="${lib}/Isid.ResonaIB.Web.BC.Utility.dll"
/>
<
include
name
="${build_dll}/Isid.ResonaIB.Web.DAC.dll"
/>
<
include
name
="${build_dll}/Isid.ResonaIB.Web.FC.dll"
/>
<
include
name
="${build_dll}/Isid.ResonaIB.Web.PARAM.dll"
/>
<
include
name
="${build_dll}/Isid.ResonaIB.Web.BC.dll"
/>
<
include
name
="${build_dll}/Isid.ResonaIB.Web.Utils.dll"
/>
</
references
>
</
csc
>
编译好了以后,记得吧编译的文件和你测试时候需要的dll文件放在同一个目录下,.NET程序运行的时候不像Java程序,Java程序只要你指定ClassPath就OK了,而.NET需要在同目录下寻找dll文件。然后就可以运行nunit任务了,代码如下所示。
<
nunit2
verbose
="${debug}"
>
<
formatter
type
="Xml"
usefile
="true"
extension
=".xml"
outputdir
="${target.path}/report"
/>
<
test
assemblyname
="${nunit_dll}/Test.Isid.ResonaIB.Web.dll"
haltonfailure
="false"
appconfig
="${root}/nunit.config"
/>
</
nunit2
>
在这里要注意设置appconfig的属性,因为默认Nunit的版本是2.2的,如果你不是这个版本,需要自己Mapping上来,在这里我们来看一下nunit.config文件如何编写,如下所示
<
configuration
>
<
runtime
>
<
assemblyBinding
xmlns
="urn:schemas-microsoft-com:asm.v1"
>
<
dependentAssembly
>
<
assemblyIdentity
name
="nunit.framework"
publicKeyToken
="96d09a1eb7f44a77"
culture
="Neutral"
/>
<
bindingRedirect
oldVersion
="2.4.1.0"
newVersion
="2.2.8.0"
/>
</
dependentAssembly
>
<
dependentAssembly
>
<
assemblyIdentity
name
="NMock2"
publicKeyToken
=""
culture
="Neutral"
/>
<
bindingRedirect
oldVersion
="1.0.2313.18049"
newVersion
="1.0.2313.18049"
/>
</
dependentAssembly
>
</
assemblyBinding
>
</
runtime
>
</
configuration
>
在这里,就可以发现NUnit测试可以顺利运行了,生成一个xml的报告文件。我们当然希望看到一个更容易看懂的报告文件,这个时候,就需要用到nunit2report任务了。不过contrib所带的nunit2report任务稍微有一点缺陷,就是采用的字符集是ISO-8859-1,如果是欧洲人,美国人的话,自然没有问题,不过对于中国人来说,就会有问题了,因为如果程序出错,错误信息是汉语或者日语的话,是无法在ISO-8859-1这个字符集下显示的,经过转换后,生成的HTML中的中文或者日文都是问号了,那么如何去解决呢。在原来的contrib下基本上是没指望了,那么我们就自己模仿contrib的nunit2report任务来写一个UTF-8格式的。C#的代码如下所示。
using
System;
using
System.Xml;
using
System.Xml.Xsl;
using
System.Xml.XPath;
using
System.Text;
using
System.Reflection;
using
System.IO;
using
System.Globalization;
using
System.Collections;
using
System.Collections.Generic;
using
NAnt.Core;
using
NAnt.Core.Util;
using
NAnt.Core.Types;
using
NAnt.Core.Attributes;
using
NAnt.Contrib.Types.NUnit2ReportMul;

namespace
NAnt.Contrib.Tasks.NUnit2ReportMul

...
{
[TaskName("nunit2reportmul")]
public class NUnit2ReportMulTask : Task

...{
// Fields
private FileSet _fileset = new FileSet();
private XmlDocument _fileSetSummary;
private ReportFormat _format = ReportFormat.NoFrames;
private string _language = "";
private string _openDescription = "no";

private static readonly ArrayList _resFiles = new ArrayList(new string[] ...{ "toolkit.xsl", "Traductions.xml", "NUnit-Frame.xsl" });
private FileSet _summaries = new FileSet();
private string _tempXmlFileSummarie = "";
private DirectoryInfo _toDir;
private FileInfo _xslFile;
private XsltArgumentList _xsltArgs;

// Methods
private XmlDocument CreateSummaryXmlDoc()

...{
XmlDocument document = new XmlDocument();
XmlElement newChild = document.CreateElement("testsummary");
newChild.SetAttribute("created", DateTime.Now.ToString());
document.AppendChild(newChild);
return document;
}

protected override void ExecuteTask()

...{
this._fileSetSummary = this.CreateSummaryXmlDoc();
foreach (string text in this.XmlFileSet.FileNames)

...{
XmlDocument document = new XmlDocument();
document.Load(text);
XmlNode newChild = this._fileSetSummary.ImportNode(document.DocumentElement, true);
this._fileSetSummary.DocumentElement.AppendChild(newChild);
}
this.Log(Level.Info, "Generating report...");
try

...{
if (!this.ToDir.Exists)

...{
this.ToDir.Create();
this.ToDir.Refresh();
}
if (this.Format == ReportFormat.NoFrames)

...{
XslTransform transform = new XslTransform();
XmlResolver resolver = new LocalResXmlResolver();
if (this.XslFile != null)

...{
transform.Load(this.LoadStyleSheet(this.XslFile), resolver);
}
else

...{
transform.Load(this.LoadStyleSheet("NUnit-NoFrame.xsl"), resolver);
}
XmlReader reader = transform.Transform(this._fileSetSummary, this._xsltArgs);
XsltArgumentList args = new XsltArgumentList();
args.AddParam("lang", "", this.Language);
XslTransform transform2 = new XslTransform();
transform2.Load(this.LoadStyleSheet("i18n.xsl"), resolver);
XPathDocument document2 = new XPathDocument(reader);
XmlTextWriter writer = new XmlTextWriter(Path.Combine(this.ToDir.FullName, "index.html"), Encoding.GetEncoding("UTF-8"));
transform2.Transform((IXPathNavigable)document2, args, (XmlWriter)writer);
reader.Close();
writer.Close();
}
else

...{
XmlTextReader reader2 = null;
try

...{
StringReader stream = new StringReader("<xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform' version='1.0' ><xsl:output method='html' indent='yes' encoding='UTF-8'/><xsl:include href="NUnit-Frame.xsl"/><xsl:template match="test-results"> <xsl:call-template name="index.html"/> </xsl:template> </xsl:stylesheet>");
this.Write(stream, Path.Combine(this.ToDir.FullName, "index.html"));
stream = new StringReader("<xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform' version='1.0' ><xsl:output method='html' indent='yes' encoding='UTF-8'/><xsl:include href="NUnit-Frame.xsl"/><xsl:template match="test-results"> <xsl:call-template name="stylesheet.css"/> </xsl:template> </xsl:stylesheet>");
this.Write(stream, Path.Combine(this.ToDir.FullName, "stylesheet.css"));
stream = new StringReader("<xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform' version='1.0' ><xsl:output method='html' indent='yes' encoding='UTF-8'/><xsl:include href="NUnit-Frame.xsl"/><xsl:template match="test-results"> <xsl:call-template name="overview.packages"/> </xsl:template> </xsl:stylesheet>");
this.Write(stream, Path.Combine(this.ToDir.FullName, "overview-summary.html"));
stream = new StringReader("<xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform' version='1.0' ><xsl:output method='html' indent='yes' encoding='UTF-8'/><xsl:include href="NUnit-Frame.xsl"/><xsl:template match="test-results"> <xsl:call-template name="all.classes"/> </xsl:template> </xsl:stylesheet>");
this.Write(stream, Path.Combine(this.ToDir.FullName, "allclasses-frame.html"));
stream = new StringReader("<xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform' version='1.0' ><xsl:output method='html' indent='yes' encoding='UTF-8'/><xsl:include href="NUnit-Frame.xsl"/><xsl:template match="test-results"> <xsl:call-template name="all.packages"/> </xsl:template> </xsl:stylesheet>");
this.Write(stream, Path.Combine(this.ToDir.FullName, "overview-frame.html"));
XPathNavigator navigator = this._fileSetSummary.CreateNavigator();
XPathExpression expr = navigator.Compile("//test-suite[(child::results/test-case)]");
XPathNodeIterator iterator = navigator.Select(expr);
while (iterator.MoveNext())

...{
string text2 = "";
XPathNavigator current = iterator.Current;
string attribute = iterator.Current.GetAttribute("name", "");
XPathNodeIterator iterator2 = current.SelectAncestors("", "", true);
string text4 = "";
for (int i = -1; iterator2.MoveNext(); i++)

...{
string text5 = iterator2.Current.GetAttribute("name", "");
if ((text5 != "") && (text5.IndexOf(".dll") < 0))

...{
text2 = text5 + "/" + text2;
}
if (i == 1)

...{
text4 = text5;
}
}
text2 = Path.Combine(this.ToDir.FullName, text2);
if (!Directory.Exists(text2))

...{
Directory.CreateDirectory(text2);
}

stream = new StringReader("<xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform' version='1.0' ><xsl:output method='html' indent='yes' encoding='UTF-8'/><xsl:include href="NUnit-Frame.xsl"/><xsl:template match="/"> <xsl:for-each select="//test-suite[@name='" + attribute + "' and ancestor::test-suite[@name='" + text4 + "'][position()=last()]]"> <xsl:call-template name="test-case"> <xsl:with-param name="dir.test">" + string.Join(".", text2.Split(new char[] ...{ '/' })) + "</xsl:with-param> </xsl:call-template> </xsl:for-each> </xsl:template> </xsl:stylesheet>");
this.Write(stream, Path.Combine(text2, attribute + ".html"));

this.Log(Level.Debug, "dir={0} Generating {1}.html", new object[] ...{ text2, attribute });
}
}
finally

...{
this.Log(Level.Debug, "Processing of stream complete.");
if (reader2 != null)

...{
reader2.Close();
}
}
}
}
catch (Exception exception)

...{
throw new BuildException("Failure generating report.", this.Location, exception);
}
}

private XsltArgumentList GetPropertyList()

...{
XsltArgumentList list = new XsltArgumentList();
this.Log(Level.Verbose, "Processing XsltArgumentList");
foreach (DictionaryEntry entry in this.Project.Properties)

...{
string parameter = entry.Value as string;
if (parameter != null)

...{
list.AddParam((string)entry.Key, "", parameter);
}
}
list.AddParam("summary.xml", "", this._tempXmlFileSummarie);
list.AddParam("open.description", "", this.OpenDescription);
return list;
}

protected override void InitializeTask(XmlNode taskNode)

...{
if (((this.Format == ReportFormat.NoFrames) && (this.XslFile != null)) && !this.XslFile.Exists)

...{

throw new BuildException(string.Format(CultureInfo.InvariantCulture, "The XSLT file "{0}" could not be found.", new object[] <