【问题标题】:Passing parameters into a unit test inside a T4 template将参数传递到 T4 模板内的单元测试中
【发布时间】:2017-10-20 15:45:45
【问题描述】:

我有以下名为 (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() #>                
        }
    }
}

【问题讨论】:

  • MyAction(Action) 是什么意思?这是委托类型?如果是这样,在模板中用作纯文本的委托类型的目的是什么?或者您的意思是这只是代表的名称(字符串)?
  • 嗨@vasiloreshenski 我的意思是一个lambda函数。
  • 你会像这样调用它 ?
  • 是的,我会改正的。但是如何将参数传递到模板中?
  • 顺便说一句,当我尝试保存模板时出现错误:命名空间不能直接包含字段或方法等成员

标签: c# unit-testing t4


【解决方案1】:

将参数注入模板可能并不容易。我认为,如果从 Visual Studio 运行 t4 模板的执行上下文是不可访问的。它们可以以编程方式执行,但我从未这样做过。

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

选项 1 - 预定义您的数据:定义匿名类型的集合(只是为了简化原型设计),其中包含测试名称和要在 t4 模板中调用的委托的两个字段,然后它从集合中生成多个测试非常容易。

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

然后从 testCases 数据生成测试。

<# 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}" #>

其中“您的模板名称.tt”是先前定义的基本 t4 模板的实际名称。

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

注意 - 注入 dll: 如果您确实需要从现有的 dll 中调用预定义的方法(您已经提到 UICoded 方法),您可以在您的模板,然后使用你需要的 dll。

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

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

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

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

【讨论】:

  • 谢谢你的例子,我会好好看看这个!
  • 可能是第一个选项和在模板中注入 UICoded 方法的程序集是实现它的可能方式。
  • 如何在代码中引用程序集?你使用例如Mydll.methodName() 我的 dll 包含我所有的命名空间,所以它很长。你知道是否可以在代码中为 dll 使用别名吗?
  • 是的,您可以像在普通 c# 类中一样使用它。全名限定符 Namespace1.Namespace2...等,或者您可以使用 将命名空间包含在 t4 模板中,然后直接使用该命名空间中的类型。
【解决方案2】:

我建议使用 NUnit,你可以从 NuGet 获得。

您可以使用TestCaseData 来发送参数化对象。也做了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
        }
    }
}

【讨论】:

  • 感谢@AzzamAziz 提供的示例。我希望我可以使用 nUnit,但我没有提到的是我正在做一个 UICoded 单元测试,不幸的是 nUnit 不支持它。我才意识到:s
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多