[英]FxCop rule around ensuring a certain method accepting a lambda is called first in a test
Using a custom FXCop rule, I want to ensure that a method is called at the top of each unit test and that all unit test code is part of an Action passed into that method. 使用自定义FXCop规则,我想确保在每个单元测试的顶部都调用一个方法,并且确保所有单元测试代码都是传递给该方法的Action的一部分。 Essentially I want this:
本质上我想要这样:
[TestMethod]
public void SomeTest()
{
Run(() => {
// ALL unit test code has to be inside a Run call
});
}
It's not hard to ensure that Run is indeed called: 确保确实调用Run并不难:
public override void VisitMethod(Method member)
{
var method = member as Method;
if (method == null || method.Attributes == null)
return;
if (method.Attributes.Any(attr => attr.Type.Name.Name == "TestMethodAttribute") &&
method.Instructions != null)
{
if (!method.Instructions.Any(i => i.OpCode == OpCode.Call || i.Value.ToString() == "MyNamespace.Run"))
{
this.Problems.Add(new Problem(this.GetResolution(), method.GetUnmangledNameWithoutTypeParameters()));
}
base.VisitMethod(method);
}
The trick is to ensure there isn't something at the top of the test that is called BEFORE the Run statement. 诀窍是确保测试的顶部没有Run语句之前的内容。 I've spent the past few hours hacking at the Instructions collection for patterns and trying to understand how to use the Body.Statements collection in code effectively.
在过去的几个小时中,我花了很多时间在指令集上研究模式,并试图了解如何在代码中有效地使用Body.Statements集。
This could also be posed as a simple IL question. 这也可以作为一个简单的IL问题提出。 I want to know a specific pattern I can validate that will accept this:
我想知道一个可以验证的特定模式,我可以接受它:
public void SomeTest()
{
Run(() => {
// Potentially lots of code
});
}
But will reject either of these: 但是将拒绝以下任何一个:
public void SomeTest()
{
String blah = “no code allowed before Run”;
Run(() => {
// Potentially lots of code
});
}
public void SomeTest()
{
Run(() => {
// Potentially lots of code
});
String blah = “no code allowed after Run”;
}
Although you can access an expression tree like structure using Method.Body
I would probably examine the instructions anyway as I have found it to get confused by common situations in the past (eg. inline array initialisation). 尽管您可以使用
Method.Body
访问类似表达式树的结构, Method.Body
我可能还是会仔细研究一下指令,因为我发现它与过去的常见情况(例如,内联数组初始化)相混淆。
How C# generates the lambda expression depends on what the lambda expression accesses: C#如何生成lambda表达式取决于lambda表达式访问的内容:
this
, then it will simply create the delegate to the instance method on this
. this
访问任何实例成员,则它将仅在this
上创建实例方法的委托。 You could probably create your own evaluation stack and trace the values through the method (I have had to do this in the past, non-trivial and a fair bit of code but not particularly difficult), but I suspect you could achieve "good enough" just by enforcing the following rules: 您可能可以创建自己的评估堆栈并通过该方法跟踪值(我过去曾经做过,虽然很简单,并且代码很少,但并不是特别困难),但是我怀疑您可以实现“足够好只需执行以下规则即可:
System.Action
and compiler generated types may be created. System.Action
和编译器生成的类型。 Run
may be called, and it must be called exactly once. Run
,并且必须恰好调用一次。 Run
must be followed by a ret
instruction (ignoring any number of nop
instructions that may appear between them). Run
的调用必须紧跟一个ret
指令(忽略它们之间可能出现的任意数量的nop
指令)。 Run
. Run
之后,任何分支指令都不能跳转到某个位置。 newobj
, call
, callvirt
, ldftn
, nop
, ret
, ldnull
newobj
, call
, callvirt
, ldftn
, nop
, ret
, ldnull
_Locals
(this is just a pseudo instruction that FxCop inserts for local variables.) _Locals
(这只是FxCop为局部变量插入的伪指令。) FxCop provides RuleUtilities.IsCompilerGenerated
to determine if a local is compiler generated, but it won't help for fields and I suspect only for locals if FxCop can find the pdb file. FxCop提供
RuleUtilities.IsCompilerGenerated
来确定本地是否由编译器生成,但是它对字段没有帮助,我怀疑如果FxCop可以找到pdb文件,则仅对本地用户有用。 You might find it easier to say "the local/field/type is compiler generated if it's type name is not a valid identifier in C#". 您可能会发现说“本地/字段/类型是编译器生成的,如果它的类型名称在C#中不是有效的标识符”会更容易说出来。
Having said all that, insisting that all tests run entirely via the Run
method seems a little arbitrary. 说了这么多,坚持所有测试都完全通过
Run
方法Run
似乎有些武断。 If the goal is for the Run
method to provide common setup/tear-down logic, there are better ways provided via nunit. 如果目标是
Run
方法提供通用的设置/拆卸逻辑,则可以通过nunit提供更好的方法。 Enforcing that people apply your action attribute is much easier than enforcing that they write their test in a particular way. 强制人们应用您的动作属性比强制他们以特定方式编写测试要容易得多。
Alternatively, you could write a Roslyn analyzer instead; 另外,您也可以编写罗斯林分析仪。 analyzers have access to the syntax tree describing how the code was written, without having to reverse engineer the structure from the IL & metadata.
分析人员可以访问描述代码编写方式的语法树,而无需对IL和元数据进行结构反向工程。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.