使用 Expression 为方法设置模拟
Setup a mock for a method using Expression
我正在尝试为方法设置模拟的 return 值。我知道这样做的经典方法,但我想通过显式创建 Expression 对象来做到这一点。到目前为止,这是我尝试过的方法:
using Moq;
using NUnit.Framework;
using System;
using System.Linq.Expressions;
namespace MoqTest
{
public interface IBlah
{
string DoStuff(string x);
}
public class SomeProgram
{
static void Main(string[] args)
{
Mock<IBlah> m = new Mock<IBlah>(MockBehavior.Strict);
//I want to do the equivalent of this:
//m.Setup(a => a.DoStuff(It.IsAny<string>())).Returns("mocked!");
var method = typeof(IBlah).GetMethod("DoStuff", new Type[] { typeof(string) });
ParameterExpression parameterForDoStuff = Expression.Parameter(typeof(string), "x");
ParameterExpression thisParameter = Expression.Parameter(typeof(IBlah), "someIBlahInstance");
MethodCallExpression methodCall = Expression.Call(thisParameter, method, new[] { parameterForDoStuff });
Expression<Func<IBlah, string>> lambdaExpression = Expression.Lambda<Func<IBlah, string>>(methodCall, new ParameterExpression[] { parameterForDoStuff });
//above line fails: Unhandled Exception: System.ArgumentException: ParameterExpression of type 'System.String' cannot be used for delegate parameter of type 'MoqTest.IBlah'
m.Setup(lambdaExpression).Returns("mocked!");
Assert.AreEqual("mocked!", m.Object.DoStuff(string.Empty));
}
}
}
如您所见,我对 lambdaExpression 感到困惑 - 我应该创建一个代表 Func<string,string>
的表达式(对于 IBlah.DoStuff 方法),还是应该创建a Func<IBlah,string>
(代表 Mock.Setup 方法的 lambda 参数)?我把我想做的等效设置放在代码的注释中。
您应该创建一个 Expression<Func<IBlah, string>>
表示 Mock.Setup
方法的表达式参数。
也就是说参数表达式应该只有一个。
此外,在构建所需的表达式时,It.IsAny<T>()
是对静态 class Moq.It
.
的通用静态方法调用
查看评论以了解如何使用反射将静态调用构建到表达式中。
[TestClass]
public class MoqExpressionTests {
[TestMethod]
public void Should_Build_Moq_Expression() {
//Arrange
//I want to do the equivalent of this:
//m.Setup(a => a.DoStuff(It.IsAny<string>())).Returns("mocked!");
var doStuff = typeof(IBlah).GetMethod("DoStuff", new Type[] { typeof(string) });
var isAnyOfString = typeof(It).GetMethod("IsAny").MakeGenericMethod(typeof(string));
// IBlah x =>
ParameterExpression parameter = Expression.Parameter(typeof(IBlah), "x");
// It.IsAny<string>()
var arg = Expression.Call(isAnyOfString);
// IBlah x => x.DoStuff(It.IsAny<string>())
MethodCallExpression body = Expression.Call(parameter, doStuff, new[] { arg });
//Func<IBlah, string> = IBlah x => x.DoStuff(It.IsAny<string>())
Expression<Func<IBlah, string>> expression =
Expression.Lambda<Func<IBlah, string>>(body, parameter);
var expected = "mocked!";
Mock<IBlah> m = new Mock<IBlah>(MockBehavior.Strict);
m.Setup(expression).Returns(expected);
var subject = m.Object;
//Act
var actual = subject.DoStuff(string.Empty);
//Assert
Assert.AreEqual(expected, actual);
}
public interface IBlah {
string DoStuff(string x);
}
}
- 你不能为 It.IsAny 使用字符串参数,需要使用 MethodCallExpression
对于lambdaExpression表达式第二个参数应该是这个参数。
public class SomeProgram
{
static void Main(string[] args)
{
Mock<IBlah> m = new Mock<IBlah>(MockBehavior.Strict);
//I want to do the equivalent of this:
//m.Setup(a => a.DoStuff(It.IsAny<string>())).Returns("mocked!");
var method = typeof(IBlah).GetMethod("DoStuff", new Type[] { typeof(string) });
MethodInfo genericIsAnyMethodInfo =
typeof(It).GetMethods().Single(e => e.Name == "IsAny").MakeGenericMethod(typeof(string));
MethodCallExpression parameterForDoStuff = Expression.Call(genericIsAnyMethodInfo);
//ParameterExpression parameterForDoStuff = Expression.Parameter(typeof(string), "x");
ParameterExpression thisParameter = Expression.Parameter(typeof(IBlah), "someIBlahInstance");
MethodCallExpression methodCall = Expression.Call(thisParameter, method, new[] { parameterForDoStuff });
Expression<Func<IBlah, string>> lambdaExpression = Expression.Lambda<Func<IBlah, string>>(methodCall, thisParameter);
//above line fails: Unhandled Exception: System.ArgumentException: ParameterExpression of type 'System.String' cannot be used for delegate parameter of type 'MoqTest.IBlah'
m.Setup(lambdaExpression).Returns("mocked!");
Assert.AreEqual("mocked!", m.Object.DoStuff(string.Empty));
}
}
我正在尝试为方法设置模拟的 return 值。我知道这样做的经典方法,但我想通过显式创建 Expression 对象来做到这一点。到目前为止,这是我尝试过的方法:
using Moq;
using NUnit.Framework;
using System;
using System.Linq.Expressions;
namespace MoqTest
{
public interface IBlah
{
string DoStuff(string x);
}
public class SomeProgram
{
static void Main(string[] args)
{
Mock<IBlah> m = new Mock<IBlah>(MockBehavior.Strict);
//I want to do the equivalent of this:
//m.Setup(a => a.DoStuff(It.IsAny<string>())).Returns("mocked!");
var method = typeof(IBlah).GetMethod("DoStuff", new Type[] { typeof(string) });
ParameterExpression parameterForDoStuff = Expression.Parameter(typeof(string), "x");
ParameterExpression thisParameter = Expression.Parameter(typeof(IBlah), "someIBlahInstance");
MethodCallExpression methodCall = Expression.Call(thisParameter, method, new[] { parameterForDoStuff });
Expression<Func<IBlah, string>> lambdaExpression = Expression.Lambda<Func<IBlah, string>>(methodCall, new ParameterExpression[] { parameterForDoStuff });
//above line fails: Unhandled Exception: System.ArgumentException: ParameterExpression of type 'System.String' cannot be used for delegate parameter of type 'MoqTest.IBlah'
m.Setup(lambdaExpression).Returns("mocked!");
Assert.AreEqual("mocked!", m.Object.DoStuff(string.Empty));
}
}
}
如您所见,我对 lambdaExpression 感到困惑 - 我应该创建一个代表 Func<string,string>
的表达式(对于 IBlah.DoStuff 方法),还是应该创建a Func<IBlah,string>
(代表 Mock.Setup 方法的 lambda 参数)?我把我想做的等效设置放在代码的注释中。
您应该创建一个 Expression<Func<IBlah, string>>
表示 Mock.Setup
方法的表达式参数。
也就是说参数表达式应该只有一个。
此外,在构建所需的表达式时,It.IsAny<T>()
是对静态 class Moq.It
.
查看评论以了解如何使用反射将静态调用构建到表达式中。
[TestClass]
public class MoqExpressionTests {
[TestMethod]
public void Should_Build_Moq_Expression() {
//Arrange
//I want to do the equivalent of this:
//m.Setup(a => a.DoStuff(It.IsAny<string>())).Returns("mocked!");
var doStuff = typeof(IBlah).GetMethod("DoStuff", new Type[] { typeof(string) });
var isAnyOfString = typeof(It).GetMethod("IsAny").MakeGenericMethod(typeof(string));
// IBlah x =>
ParameterExpression parameter = Expression.Parameter(typeof(IBlah), "x");
// It.IsAny<string>()
var arg = Expression.Call(isAnyOfString);
// IBlah x => x.DoStuff(It.IsAny<string>())
MethodCallExpression body = Expression.Call(parameter, doStuff, new[] { arg });
//Func<IBlah, string> = IBlah x => x.DoStuff(It.IsAny<string>())
Expression<Func<IBlah, string>> expression =
Expression.Lambda<Func<IBlah, string>>(body, parameter);
var expected = "mocked!";
Mock<IBlah> m = new Mock<IBlah>(MockBehavior.Strict);
m.Setup(expression).Returns(expected);
var subject = m.Object;
//Act
var actual = subject.DoStuff(string.Empty);
//Assert
Assert.AreEqual(expected, actual);
}
public interface IBlah {
string DoStuff(string x);
}
}
- 你不能为 It.IsAny 使用字符串参数,需要使用 MethodCallExpression
对于lambdaExpression表达式第二个参数应该是这个参数。
public class SomeProgram { static void Main(string[] args) { Mock<IBlah> m = new Mock<IBlah>(MockBehavior.Strict); //I want to do the equivalent of this: //m.Setup(a => a.DoStuff(It.IsAny<string>())).Returns("mocked!"); var method = typeof(IBlah).GetMethod("DoStuff", new Type[] { typeof(string) }); MethodInfo genericIsAnyMethodInfo = typeof(It).GetMethods().Single(e => e.Name == "IsAny").MakeGenericMethod(typeof(string)); MethodCallExpression parameterForDoStuff = Expression.Call(genericIsAnyMethodInfo); //ParameterExpression parameterForDoStuff = Expression.Parameter(typeof(string), "x"); ParameterExpression thisParameter = Expression.Parameter(typeof(IBlah), "someIBlahInstance"); MethodCallExpression methodCall = Expression.Call(thisParameter, method, new[] { parameterForDoStuff }); Expression<Func<IBlah, string>> lambdaExpression = Expression.Lambda<Func<IBlah, string>>(methodCall, thisParameter); //above line fails: Unhandled Exception: System.ArgumentException: ParameterExpression of type 'System.String' cannot be used for delegate parameter of type 'MoqTest.IBlah' m.Setup(lambdaExpression).Returns("mocked!"); Assert.AreEqual("mocked!", m.Object.DoStuff(string.Empty)); } }