Скажем, у меня есть такой метод:
public void Method<T>(Func<T> func)
{
...
}
Можно ли как-нибудь использовать API Expression
и эффективно внедрить некоторый код для запуска до запуска кода из переданного в 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 все, что передается, я хочу заранее «вставить» некоторый код, чтобы я мог видеть, какие значения передаются, я могу добавить некоторые данные в некоторый словарь, чтобы сохранить отслеживание количества вызовов методов и т. д.
Надеюсь, теперь это имеет больше смысла ...