首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在设计中或在构建时生成方法(C#)

在设计中或在构建时生成方法(C#)
EN

Stack Overflow用户
提问于 2011-12-15 16:02:09
回答 4查看 1.2K关注 0票数 3

我有一个集成测试解决方案。我用XML文件描述了我的测试。为了利用Visual 2010测试基础结构,我有一个C#类,其中每个XML测试文件都有一个关联的方法来加载该XML文件并执行其内容。看起来是这样的:

代码语言:javascript
复制
[TestClass]
public class SampleTests
{

    [TestMethod]
    public void Test1()
    {
        XamlTestManager.ConductTest();
    }

    [TestMethod]
    public void Test2()
    {
        XamlTestManager.ConductTest();
    }

    ...

    [TestMethod]
    public void TestN()
    {
        XamlTestManager.ConductTest();
    }
}

每个方法名称对应于一个XML文件名。因此,我必须在我的测试目录中有以下文件:

  • Test1.xml
  • Test2.xml
  • ..。
  • TestN.xml

XamlTestManager.ConductTest()使用StackTrace类获取调用方法的名称,这样就可以找到要加载的正确的StackTrace测试文件。

每当我更改测试、添加/删除/重命名XML测试文件时,我都希望摆脱添加/删除/重命名测试方法的额外管理。如何在编译过程中根据测试目录中的实际XML文件自动生成这个类或其方法?

选项1:我考虑过PostSharp,但它不允许我动态地查找XML文件并生成方法(或者我只是肤浅的?)。

选项2:另一个想法是构建一个Visual自定义工具,它在执行时生成我的代码。这里的缺点是部署。自定义工具需要注册到VS。我想要一个解决方案,可以提交到存储库,检查它到另一台计算机,并立即使用它。)我相信简单。“签出并运行”只是大大简化了新开发人员的生活,如果他们在编译运行应用程序之前不需要经过一系列的安装。)

您有什么建议,如何解决不必要的维护问题?

编辑:

为了贾斯汀的要求,我补充了更多的细节。我们使用Bizunit (太棒了!)作为我们框架的基础,用一卡车的定制做了高级别的测试步骤。通过这些步骤,我们可以像从乐高块构建测试一样,以声明的方式构建测试。我们的步骤包括FileDrop、WebService调用甚至轮询,启动一个完整的web服务器来模拟合作伙伴web应用程序、随机数据生成器、数据比较步骤等等。下面是一个测试xml的示例(实际上是XAML):

代码语言:javascript
复制
<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>

这就是它所做的:

  1. 调用对测试主题的明确操作。
  2. 在端口4000上启动一个webserver,作为一个名为MyProviderService的模拟合作伙伴应用程序。
  3. 通过HTTP调用测试主题来轮询模拟合作伙伴
  4. 从两个随机生成的内容中创建包含geo信息的新文档。
  5. 将文档推送给模拟合作伙伴-因此,测试对象将通过轮询来获取它。
  6. 测试在文档上应用geo过滤器。
  7. 深度比较步骤加载过滤后的文档作为比较的基础,并通过web服务加载测试对象的内容。
  8. 作为清理,它通过HTTP步骤停止轮询,并杀死模拟合作伙伴的web服务器。

Bizunit的强大功能是将在C#中创建测试的方便性与intellisense结合起来,并便于在XAML文件中维护/复制测试。有关其工作原理的简单快速阅读:http://kevinsmi.wordpress.com/2011/03/22/bizunit-4-0-overview/

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2011-12-15 16:28:39

正如@GeorgeDuckett所说,T4模板可能是前进的道路。在我正在开发的应用程序中,我们使用它们很多,包括生成存储库、服务、ViewModels、Enum和最近的单元测试。

它们基本上是用VB或C#编写的代码生成脚本,查找XML文件目录对于这类模板来说没有问题。

如果你真的选择走T4路线,有形T4编辑器绝对是一个必须拥有的,它是免费下载的。

下面是一个T4脚本的快速示例,它应该执行或非常接近您想要的内容:

代码语言:javascript
复制
<#@ 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模板的顶部,在<#@模板. #>行下:

代码语言:javascript
复制
<#@ assembly name="EnvDTE" #>
<#@ import namespace="EnvDTE" #>

然后,在模板中,您可以访问和使用,如下所示:

代码语言:javascript
复制
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);
    }
}

outDirtargetPath包含输出目录和输出文件的完整路径。

票数 1
EN

Stack Overflow用户

发布于 2011-12-15 16:28:14

与其为每一组测试数据创建单独的测试,还可以为每组测试数据创建一个重复运行的测试:

代码语言:javascript
复制
[TestClass]
public class SampleTests
{
    [TestMethod]
    public void Test()
    {
        for (var i = 0; i < 10; ++i)
            XamlTestManager.ConductTest(i); 
    }
}

您还可以使用DataSource属性执行数据驱动的测试。这将对数据集中的每一行执行测试。

代码语言:javascript
复制
[TestClass]
public class SampleTests
{
    public TestContext Context { get; set; }

    [TestMethod]
    [DataSource(...)]
    public void Test()
    {
        var someData = Context.DataRow["SomeColumnName"].ToString();
        ...
    }
}
票数 1
EN

Stack Overflow用户

发布于 2011-12-15 16:37:56

实际上,我不认为这是构建时代码生成的工作,我认为在这种情况下,您应该使用数据属性来驱动测试。

如果您使用xunit,您可以这样做:

代码语言:javascript
复制
public class SampleTests
{
    [Theory]
    [InlineData(1)]
    [InlineData(2)]
    [InlineData(...)]
    [InlineData(N)]
    public void Test(int x)
    {
        XamlTestManager.ConductTest(x);
    }
}

并且它将在每个InlineData属性上运行一次测试。此外,我相信还有另一个属性,您可以传递一个路径到一个文件,它将填充您的参数值从该文件.

我认为NUnit有类似的特性,但是XUnit要好得多,我建议使用XUnit。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/8522947

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档