将参数传递到 T4 模板内的单元测试中

Passing parameters into a unit test inside a T4 template

我有以下名为 (MyTest.tt)T4 模板文件,其中 TestName(String) 和MyAction(Action) 是参数。我可以将 lambda 操作传递到模板中吗? 如何将数据传递给参数?

<#@ template debug="false" hostspecific="false" language="C#" #>  
<#@ assembly name="System.Core" #>
<#@ assembly name="$(TargetPath)" #>
<#@ output extension=".Generated.cs" #>
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;

namespace Test.TestTemplate
{
    [TestClass]
    public class Test2
    {
        [TestMethod]
        public void HAS_ACCESS_<#= "TestName" #>()
        {                       
            <#= MyAction.Invoke() #>                
        }
    }
}

我建议为此使用 NUnit,您可以从 NuGet 获得它。

您可以使用TestCaseData in order to send parameterized objects. It has also been done here

注意:我不使用 T4。您可能需要稍微调整一下代码,但就是这个想法。

namespace Test.TestTemplate
{
    [TestFixture]
    [TestCaseSource(typeof(TestTemplateData), "HasAccessData")]
    // Make the type as bool so you can run it against the NUnit .Returns
    public bool HAS_ACCESS_<#= "TestName" #>(string stringParameter, Action actionParameter)
    {
        <#= MyAction.Invoke() #>

        // Do something here with the parameters...
        // Remember you have to return here so the .Returns can assert against it.
        // No need to do Assert
    }
}

然后是包含你的测试数据的数据源文件

public class TestTemplateData
{
    // I assume you will use the same action all the time, so just
    // define it here
    private Action<int> square = (int x) => x * x;

    public static IEnumerable HasAccessData
    {
        get
        {
            yield return new TestCaseData
            (
                // This is the string parameter
                "Word",
                // Put whatever parameter you want for your action
                square(1)
            )
            .SetName("Name of this test case") // Test name
            .Returns(true); // What is the test expected to return

            // You can return multiple test cases this way
            yield return new TestCaseData
            (
                "Word",
                square(2)
            )
            .SetName("Name of this test case") // Test name
            .Returns(true); // What is the test expected to return
        }
    }
}

将参数注入模板可能不是那么容易。我认为,如果您 运行 来自 visual studio 的 t4 模板的执行上下文不可访问。它们可以通过编程方式执行,但我从来没有这样做过。

如果你尝试使用参数化的 t4 模板,我现在可以想到几个选项。

选项 1 - 预定义您的数据:定义匿名类型的集合(只是为了更容易制作原型),其中包含两个字段,用于测试名称和要在您的代码中调用的委托t4模板,那么很容易从集合中生成多个测试。

<#
// define the collection
var testCases = new [] {
 new {name = "TestCase1", method = new Action(() => { /* your action body */ }) },
 etc
};
#> 

然后从测试用例数据生成测试。

<# foreach(var testCase in testCases) { #>
   [TestMethod]
   public void HAS_ACCESS_<#= testCase.name #> ()
   {
        <#= testCase.method() #>
   }

<# } #>

选项 2 - 共享模板: 您可以定义基本的 t4 模板,该模板可以从另一个模板中包含和执行。这可以是您的基本模板

 [TestClass]
 public class Test2
 {
    [TestMethod]
      public void HAS_ACCESS_<#= TestName #>()
      {                       
       <#= MyAction.Invoke() #>                
      }
 }

请注意,使用 TestNameMyAction 是因为它们是模板中定义的变量。现在在第二个模板中,您可以执行以下操作

<#
    string TestName = "TestCase1";
    Action MyAction = () => { };
#>
<#@ include file="{Yor tempalte name.tt}" #>

其中 'Your template name.tt' 是之前定义的基础 t4 模板的实际名称。

通过这种方式,您可以定义多个 t4 模板,这些模板将使用提供的参数调用基本模板。

注意 - 注入 dll: 如果你真的需要从已经存在的 dll 中调用预定义方法(你提到了 UICoded 方法)可以在你的模板中包含如下dll,然后使用你需要的dll。

<#@ assembly name="$(TargetDir)Mydll.dll" #>

<#@ assembly name="C:/..../Mydll.dll" #> 

当我需要从预定义数据生成多个测试时(当我知道我的测试用例但手工工作太多可以自动化时),我使用第一个选项

我在生成 c# 类 时使用的 第二个选项 (对于单元测试生成而言不是那么多)并且不希望所有这些都是在一个文件中。