我有一个集成测试解决方案。我用XML文件描述了我的测试。为了利用Visual 2010测试基础结构,我有一个C#类,其中每个XML测试文件都有一个关联的方法来加载该XML文件并执行其内容。看起来是这样的:
[TestClass]
public class SampleTests
{
[TestMethod]
public void Test1()
{
XamlTestManager.ConductTest();
}
[TestMethod]
public void Test2()
{
XamlTestManager.ConductTest();
}
...
[TestMethod]
public void TestN()
{
XamlTestManager.ConductTest();
}
}每个方法名称对应于一个XML文件名。因此,我必须在我的测试目录中有以下文件:
XamlTestManager.ConductTest()使用StackTrace类获取调用方法的名称,这样就可以找到要加载的正确的StackTrace测试文件。
每当我更改测试、添加/删除/重命名XML测试文件时,我都希望摆脱添加/删除/重命名测试方法的额外管理。如何在编译过程中根据测试目录中的实际XML文件自动生成这个类或其方法?
选项1:我考虑过PostSharp,但它不允许我动态地查找XML文件并生成方法(或者我只是肤浅的?)。
选项2:另一个想法是构建一个Visual自定义工具,它在执行时生成我的代码。这里的缺点是部署。自定义工具需要注册到VS。我想要一个解决方案,可以提交到存储库,检查它到另一台计算机,并立即使用它。)我相信简单。“签出并运行”只是大大简化了新开发人员的生活,如果他们在编译运行应用程序之前不需要经过一系列的安装。)
您有什么建议,如何解决不必要的维护问题?
编辑:
为了贾斯汀的要求,我补充了更多的细节。我们使用Bizunit (太棒了!)作为我们框架的基础,用一卡车的定制做了高级别的测试步骤。通过这些步骤,我们可以像从乐高块构建测试一样,以声明的方式构建测试。我们的步骤包括FileDrop、WebService调用甚至轮询,启动一个完整的web服务器来模拟合作伙伴web应用程序、随机数据生成器、数据比较步骤等等。下面是一个测试xml的示例(实际上是XAML):
<TestCase BizUnitVersion="4.0.154.0" Name="StackOverflowSample" xmlns="clr-namespace:BizUnit.Xaml;assembly=BizUnit" xmlns:nib="clr-namespace:MyCompany.IntegrationTest;assembly=BizUnit.MyCustomSteps" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<TestCase.SetupSteps>
<nib:ClearStep FailOnError="True" RunConcurrently="False" />
<nib:LaunchSimulatedApp AppKernelCacheKey="provider" FailOnError="True" FireWakeUpCall="False" PortNumber="4000" RepresentedSystem="MyProviderService" RunConcurrently="False" />
<nib:HttpGetStep FailOnError="True" RunConcurrently="False" Url="http://localhost:10000/Home/StartSvgPolling">
<nib:HttpGetStep.Parameters>
<x:String x:Key="PolledAddress">http://localhost:4000/SvgOutputPort.asmx</x:String>
<x:String x:Key="PollingInterval">10</x:String>
<x:String x:Key="FilterFile"></x:String>
</nib:HttpGetStep.Parameters>
</nib:HttpGetStep>
</TestCase.SetupSteps>
<TestCase.ExecutionSteps>
<nib:DocumentMergeStep FailOnError="True" OutputCacheKey="inputDocument" RunConcurrently="False">
<nib:DocumentMergeStep.InputDocuments>
<nib:RandomLoader BoundingBox="Europe" LinkbackUrlPattern="http://MyProviderService/id={0}" MaxAmount="10" MaxID="100" MinAmount="10" MinID="0" NamePattern="EuropeanObject_{0}" NativeFormat="Svg" RepeatableRandomness="False" UriPrefix="European" />
<nib:RandomLoader BoundingBox="PacificIslands" LinkbackUrlPattern="http://MyProviderService/id={0}" MaxAmount="10" MaxID="100" MinAmount="10" MinID="0" NamePattern="PacificObject_{0}" NativeFormat="Svg" RepeatableRandomness="False" UriPrefix="Pacific" />
</nib:DocumentMergeStep.InputDocuments>
</nib:DocumentMergeStep>
<nib:PushToSimulatedApp AppKernelCacheKey="provider" ContentFormat="Svg" FailOnError="True" RunConcurrently="False">
<nib:PushToSimulatedApp.InputDocument>
<nib:CacheLoader SourceCacheKey="inputDocument" />
</nib:PushToSimulatedApp.InputDocument>
</nib:PushToSimulatedApp>
<nib:GeoFilterStep FailOnError="True" OutputCacheKey="filteredDocument" RunConcurrently="False" SelectionBox="Europe">
<nib:GeoFilterStep.InputDocument>
<nib:CacheLoader SourceCacheKey="inputDocument" />
</nib:GeoFilterStep.InputDocument>
</nib:GeoFilterStep>
<nib:DeepCompareStep DepthOfComparision="ID, Geo_2MeterAccuracy, PropertyBag, LinkbackUrl" FailOnError="True" RunConcurrently="False" Timeout="30000" TolerateAdditionalItems="False">
<nib:DeepCompareStep.ReferenceSource>
<nib:CacheLoader SourceCacheKey="filteredDocument" />
</nib:DeepCompareStep.ReferenceSource>
<nib:DeepCompareStep.InvestigatedSource>
<nib:SvgWebServiceLoader GeoFilter="Europe" NvgServiceUrl="http://localhost:10000/SvgOutputPort.asmx"/>
</nib:DeepCompareStep.InvestigatedSource>
</nib:DeepCompareStep>
</TestCase.ExecutionSteps>
<TestCase.CleanupSteps>
<nib:HttpGetStep FailOnError="True" RunConcurrently="False" Url="http://localhost:10000/Home/StopSvgPolling">
<nib:HttpGetStep.Parameters>
<x:String x:Key="PolledAddress">http://localhost:4000/SvgOutputPort.asmx</x:String>
</nib:HttpGetStep.Parameters>
</nib:HttpGetStep>
<nib:KillSimulatedApp AppKernelCacheKey="provider" FailOnError="True" PortNumber="4000" RunConcurrently="False" />
</TestCase.CleanupSteps>
</TestCase>这就是它所做的:
Bizunit的强大功能是将在C#中创建测试的方便性与intellisense结合起来,并便于在XAML文件中维护/复制测试。有关其工作原理的简单快速阅读:http://kevinsmi.wordpress.com/2011/03/22/bizunit-4-0-overview/
发布于 2011-12-15 16:28:39
正如@GeorgeDuckett所说,T4模板可能是前进的道路。在我正在开发的应用程序中,我们使用它们很多,包括生成存储库、服务、ViewModels、Enum和最近的单元测试。
它们基本上是用VB或C#编写的代码生成脚本,查找XML文件目录对于这类模板来说没有问题。
如果你真的选择走T4路线,有形T4编辑器绝对是一个必须拥有的,它是免费下载的。
下面是一个T4脚本的快速示例,它应该执行或非常接近您想要的内容:
<#@ template language="C#" debug="true" hostspecific="true"#>
<#@ output extension="g.cs"#>
[TestClass]
public class SampleTests
{
<#
string[] files = Directory.GetFiles(@"C:\TestFiles", "*.xml");
foreach(string filePath in files)
{
string fileName = Path.GetFileNameWithoutExtension(filePath);
#>
[TestMethod]
public void <#=fileName#>()
{
XamlTestManager.ConductTest();
}
<#
}
#>
}确保这是一个具有.tt扩展名的文件,然后在该文件的属性窗口上,确保Build是None,自定义工具是TextTemplatingFileGenerator。
编辑:从T4模板访问输出目录
将以下两行添加到T4模板的顶部,在<#@模板. #>行下:
<#@ assembly name="EnvDTE" #>
<#@ import namespace="EnvDTE" #>然后,在模板中,您可以访问和使用,如下所示:
IServiceProvider serviceProvider = this.Host as IServiceProvider;
DTE dte = serviceProvider.GetService(typeof(DTE)) as DTE;
object[] activeSolutionProjects = dte.ActiveSolutionProjects as object[];
if(activeSolutionProjects != null)
{
Project project = activeSolutionProjects[0] as Project;
if(project != null)
{
Properties projectProperties = project.Properties;
Properties configurationProperties = project.ConfigurationManager.ActiveConfiguration.Properties;
string projectDirectory = Path.GetDirectoryName(project.FullName);
string outputPath = configurationProperties.Item("OutputPath").Value.ToString();
string outputFile = projectProperties.Item("OutputFileName").Value.ToString();
string outDir = Path.Combine(projectDirectory, outputPath);
string targetPath = Path.Combine(outDir, outputFile);
}
}outDir和targetPath包含输出目录和输出文件的完整路径。
发布于 2011-12-15 16:28:14
与其为每一组测试数据创建单独的测试,还可以为每组测试数据创建一个重复运行的测试:
[TestClass]
public class SampleTests
{
[TestMethod]
public void Test()
{
for (var i = 0; i < 10; ++i)
XamlTestManager.ConductTest(i);
}
}您还可以使用DataSource属性执行数据驱动的测试。这将对数据集中的每一行执行测试。
[TestClass]
public class SampleTests
{
public TestContext Context { get; set; }
[TestMethod]
[DataSource(...)]
public void Test()
{
var someData = Context.DataRow["SomeColumnName"].ToString();
...
}
}发布于 2011-12-15 16:37:56
实际上,我不认为这是构建时代码生成的工作,我认为在这种情况下,您应该使用数据属性来驱动测试。
如果您使用xunit,您可以这样做:
public class SampleTests
{
[Theory]
[InlineData(1)]
[InlineData(2)]
[InlineData(...)]
[InlineData(N)]
public void Test(int x)
{
XamlTestManager.ConductTest(x);
}
}并且它将在每个InlineData属性上运行一次测试。此外,我相信还有另一个属性,您可以传递一个路径到一个文件,它将填充您的参数值从该文件.
我认为NUnit有类似的特性,但是XUnit要好得多,我建议使用XUnit。
https://stackoverflow.com/questions/8522947
复制相似问题