将参数传递到 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() #>
}
}
请注意,使用 TestName 和 MyAction 是因为它们是模板中定义的变量。现在在第二个模板中,您可以执行以下操作
<#
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# 类 时使用的 第二个选项 (对于单元测试生成而言不是那么多)并且不希望所有这些都是在一个文件中。
我有以下名为 (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() #>
}
}
请注意,使用 TestName 和 MyAction 是因为它们是模板中定义的变量。现在在第二个模板中,您可以执行以下操作
<#
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# 类 时使用的 第二个选项 (对于单元测试生成而言不是那么多)并且不希望所有这些都是在一个文件中。