说我有这样的方法:
public void Method<T>(Func<T> func)
{
...
}
有什么方法可以使用Expression
API 并在传入的代码运行之前有效地注入一些代码来Func
运行?这意味着,在我的方法中,我不会实际执行Func
,而是为它分配一个对象的一些属性,该对象将调用Func
. 我只想在传入的代码运行之前运行一些代码。另外,我的代码是否有可能从 中“返回” Func
,从而使传入的代码永远不会运行?
编辑:好的,我担心这还不够。这就是我想要做的。使用 Moles,您可以删除代码中的任何调用。所以说我有一些不可测试的代码调用静态方法,如下所示:
FileSystem.ReadAllText(string fileName);
痣将创建一个存根/痣对象(无论术语是什么),然后我可以用它来存根方法,所以当我的代码调用 ReadAllText 时,它会调用我的存根:
MFileSytem.ReadAllTextString = s => return "content";
非常酷的东西,但是我正在尝试扩展这个想法,这样我就可以像使用典型的 Mocking 框架(如 Moq)一样使用它,这样我就可以只在传入的参数是某个值时才删除该方法,或者可能验证一个方法只被调用一次等。这是我到目前为止所拥有的:
仍然非常非常粗糙!
public static class Extensions
{
public static void TestWithMoles<T, U, V>(this T item, Expression<Func<U, V>> expression, MolesDelegates.Func<U, V> stub)
{
var methodCallExpression = expression.Body as MethodCallExpression;
if (methodCallExpression != null)
{
var method = methodCallExpression.Method;
var returnType = method.ReturnType.Name;
var assemblyName = method.DeclaringType.Assembly;
var assmebly = GetMolesAssmeblyName(assemblyName.GetName().Name);
var type = assmebly.GetTypes().FirstOrDefault(t => t.Name == "M" + method.DeclaringType.Name);
var property = type.GetProperties(BindingFlags.Static | BindingFlags.Public).FirstOrDefault(p => p.Name == method.Name + returnType);
property.SetValue(item, stub, null);
}
}
private static Assembly GetMolesAssmeblyName(string assemblyName)
{
var entryAssembly = Assembly.GetExecutingAssembly();
foreach (var assembly in entryAssembly.GetReferencedAssemblies())
{
if (assembly.Name == assemblyName)
{
continue;
}
if (assembly.Name.Contains(assemblyName) && assembly.Name.Contains(".Moles"))
{
return Assembly.Load(assembly.FullName);
}
}
return null;
}
}
现在的想法是,如果我有这样的课程:
public class TestReader
{
public string Content { get; private set; }
public void LoadFile(string fileName)
{
var content = FileSystem.ReadAllText(fileName);
if (!content.StartsWith("test"))
{
throw new ArgumentException("invalid file");
}
this.Content = content;
}
}
我现在可以这样做:
[TestMethod]
[HostType("Moles")]
public void CheckValidFilewithMoles()
{
//arrange
var fileName = "test.txt";
var content = "test";
//act
var test = new TestReader();
test.TestWithMoles<TestReader, string, string>(s => FileSystem.ReadAllText(s), s =>
{
Assert.IsTrue(s == fileName);
return content;
});
test.LoadFile(fileName);
//assert
Assert.AreEqual(content, test.Content);
}
到目前为止一切顺利,但我还没有完成任何事情。我想做的是,在我的扩展方法中,当我这样做时:
property.SetValue(item, stub, null);
这会将 Moles 委托设置为传入的任何内容,我想事先“注入”一些代码,以便我可以看到传入的值是什么,我可以将一些数据添加到某个字典中以跟踪方法调用的计数等。
希望现在更有意义...