我有一个集成测试解决方案。我在XML文件中描述了我的测试。为了利用Visual Studio 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类获取调用方法的名称,这样它就可以找到要加载的正确XML测试文件。
每当我更改测试,添加/删除/重命名XML测试文件时,我想摆脱添加/删除/重命名测试方法的额外管理。 如何根据测试目录中的实际XML文件在编译过程中自动生成此类或其方法?
选项1: 我考虑过PostSharp,但它不允许我查找XML文件并动态生成方法(或者我是肤浅的?)。 选项2: 另一个想法是构建一个Visual Studio自定义工具,无论何时执行它都会生成代码。这里的缺点是部署。自定义工具需要注册到VS.我想要一个可以提交到存储库的解决方案,将其签出到另一台计算机并立即使用它。 (我相信简单。“检查并运行”只是简单地简化了新开发人员的生活,如果他们在编译运行应用程序之前不需要经过安装的事情列表。)
您有什么建议,如何摆脱不必要的维护问题?
修改
对于Justin的请求,我添加了更多细节。我们使用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#中创建测试的难易程度与智能感知相结合,并且易于在XAML文件中维护/复制它。有关其工作原理的快速简单阅读:http://kevinsmi.wordpress.com/2011/03/22/bizunit-4-0-overview/
答案 0 :(得分:1)
您可以创建针对每组测试数据重复运行的单个测试,而不是为每组测试数据创建单独的测试:
[TestClass]
public class SampleTests
{
[TestMethod]
public void Test()
{
for (var i = 0; i < 10; ++i)
XamlTestManager.ConductTest(i);
}
}
您还可以使用DataSource attribute执行数据驱动的测试。这将对数据集中的每一行执行测试。
[TestClass]
public class SampleTests
{
public TestContext Context { get; set; }
[TestMethod]
[DataSource(...)]
public void Test()
{
var someData = Context.DataRow["SomeColumnName"].ToString();
...
}
}
答案 1 :(得分:1)
正如@GeorgeDuckett所说,T4模板可能是要走的路。在我正在开发的应用程序中,我们经常使用它们,包括生成存储库,服务,ViewModel,枚举和最近的单元测试。
它们基本上是用VB或C#编写的代码生成脚本,查看XML文件的目录对于这些类型的模板都没有问题。
如果您确实选择了T4路线,Tangible T4 Editor肯定是必须的,可以免费下载。
以下是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的文件中,然后在此文件的属性窗口中,确保构建操作为None
,自定义工具为TextTemplatingFileGenerator
。
编辑:从T4模板访问输出目录
在&lt;#@ template ...#&gt;下添加以下两行到T4模板的顶部。行:
<#@ assembly name="EnvDTE" #>
<#@ import namespace="EnvDTE" #>
然后在模板中,您可以访问和使用visual studio API,如下所示:
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
包含输出目录和输出文件的完整路径。
答案 2 :(得分:0)
我实际上并不认为这是构建时代码生成的工作,我认为在这种情况下你应该使用数据属性来驱动测试。
如果您使用xunit,您可以这样做:
public class SampleTests
{
[Theory]
[InlineData(1)]
[InlineData(2)]
[InlineData(...)]
[InlineData(N)]
public void Test(int x)
{
XamlTestManager.ConductTest(x);
}
}
它将根据InlineData属性运行一次测试。另外我相信还有另一个属性,您可以将路径传递给文件,它将使用该文件中的值填充参数...
我认为NUnit有类似功能,但XUnit要好得多,我建议改用XUnit。
答案 3 :(得分:0)
刚才回答“使用T4从XML生成代码”问题。
https://stackoverflow.com/a/8554949/753110
您的要求与我们最初所做的完全匹配(以及导致在该答案中描述的ADM的发现)。
我们目前正致力于基于测试用例的生成,其中测试用例实际上是由测试人员构建的,但是通过代码生成完整的集成测试以支持它们。
如果您想查看以下内容,请为其他示例添加基于XML的自定义生成演示: