我试图找到一个这样的例子,但没有成功,所以这就是为什么我问这个问题。
让我们从一些代码开始。下面是我的代码:
class Dummy
{
public void DoDummyThings1()
{
Console.WriteLine("Sorry, I'm dummy 1...");
}
public void DoDummyThings2()
{
Console.WriteLine("Sorry, I'm dummy 2...");
}
public void DoDummyThings3()
{
Console.WriteLine("Sorry, I'm dummy 3...");
}
}
我的测试代码:
[TestClass]
public class UnitTest
{
private Dummy dum = new Dummy();
[TestInitialize()]
public void SetUp()
{
MethodInfo mi = typeof (UnitTest).GetMethod("TestDummy");
MethodBody mb = mi.GetMethodBody();
}
[TestMethod]
public void TestDummy()
{
this.dum.DoDummyThings1();
this.dum.DoDummyThings2();
this.dum.DoDummyThings3();
}
}
这就是我要做的。我想在执行每个测试方法之前,查看测试方法并检查Dummy类的DoDummyThings1,DoDummyThings2和DoDummyThings3方法是否会被调用。
这样做的目的是,根据调用的DoDummyThingsX方法的不同,我希望在代码深处的某个地方注入不同的实现,以便在运行时修改某些类的行为(将接口的注入实现交换为另一个)。
有人能告诉我如何正确地做到这一点(用最新版本的Cecil或c#的其他东西)?有没有办法做到这一点,而不使用。dll文件?(目前,这是我想出如何做到这一点的唯一方法,但是,使用字符串作为"MyDllName.dll"one_answers"MyNamespace"。MyClassName"硬编码对我来说是不可能的)
我已经知道的其他stackoverflow线程:
- 查看是否使用反射在方法内部调用方法
- 如何确定在一个方法中调用哪些方法?
- 我可以使用反射来检查方法中的代码吗?
谁能帮我一个完整(但简单)的例子(如果可能的话)?谢谢你!
这个答案演示了如何确定哪些测试执行了Dummy方法,但没有回答:
在代码深处注入不同的实现,以便在运行时修改某些类的行为
反射不提供你需要的对单元测试方法的IL Body的粒度访问;但是Cecil
提供了这个功能。下面的linq返回一个内部调用DoDummyThings1
的方法列表。linq可以更有效,但我想让它尽可能清楚。where
条款是其中的重要组成部分。
//syntax based on version 0.9.5.4 (http://nuget.org/packages/Mono.Cecil/0.9.5.4)
using Mono.Cecil;
using Mono.Cecil.Cil;
//...
string assemblyPath = (@"path to your unit test assemblyMyTests.dll");
AssemblyDefinition asm = AssemblyDefinition.ReadAssembly(assemblyPath);
List<MethodDefinition> testsThatCallDummyMethods =
(from mod in asm.Modules
from t in mod.Types
from meth in t.Methods
where meth.HasBody
from instr in meth.Body.Instructions
let op = instr.Operand as MethodDefinition
where
instr.OpCode == OpCodes.Callvirt &&
op != null &&
op.DeclaringType.FullName ==
"Lib.Dummy" //namespace qualified type name
&& op.Name ==
"DoDummyThings1" //method names...
select meth)
.ToList();
用ILDasm
拆卸测试程序集,找出操作码/操作数。TestDummy
方法的相关部分类似于:
//this.dum.DoDummyThings1();
IL_0001: ldarg.0
IL_0002: ldfld class Lib.Dummy Lib.UnitTest::dum
IL_0007: callvirt instance void Lib.Dummy::DoDummyThings1()