给定一个对象,我想创建一个实现对象接口并模拟一个方法的模拟,但将其余方法转发给真实对象,而不是基类。
例如:
ISqlUtil sqlUtil = GetTheRealSqlUtilObjectSomehow(...);
var mock = new Mock<ISqlUtil>();
mock.Setup(o => o.SpecialMethodToBeMocked(...)).Returns<...>(...)
// Here I would like to delegate the rest of the methods to the real sqlUtil object. How ?
Run Code Online (Sandbox Code Playgroud)
因此,在示例中,我只想模拟ISqlUtil.SpecialMethodToBeMocked并将其余方法/属性转发到现有实例sqlUtil。
在 Moq.NET 中可能吗?
编辑 1
它也应该适用于泛型方法。
你不能用开箱即用的 Moq 来做到这一点。但是,我认为如果您深入到下一层并直接使用 Castle DynamicProxy(这是 Moq 下的内容),您基本上可以实现您想要的。
因此,给定以下基本代码来模拟您的问题(本质上,一个接口、一个具体实现和一个工厂,因为具体很难制作/设置):
public interface ISqlUtil {
T SomeGenericMethod<T>(T args);
int SomeMethodToIntercept();
}
public class ConcreteSqlUtil : ISqlUtil {
public T SomeGenericMethod<T>(T args){
return args;
}
public int SomeMethodToIntercept() {
return 42;
}
}
public class SqlUtilFactory {
public static ISqlUtil CreateSqlUtil() {
var rVal = new ConcreteSqlUtil();
// Some Complex setup
return rVal;
}
}
Run Code Online (Sandbox Code Playgroud)
然后,您可以进行以下测试:
public void TestCanInterceptMethods() {
// Create a concrete instance, using the factory
var coreInstance = SqlUtilFactory.CreateSqlUtil();
// Test that the concrete instance works
Assert.AreEqual(42, coreInstance.SomeMethodToIntercept());
Assert.AreEqual(40, coreInstance.SomeGenericMethod(40));
// Create a proxy generator (you'll probably want to put this
// somewhere static so that it's caching works if you use it)
var generator = new Castle.DynamicProxy.ProxyGenerator();
// Use the proxy to generate a new class that implements ISqlUtil
// Note the concrete instance is passed into the construction
// As is an instance of MethodInterceptor (see below)
var proxy = generator.CreateInterfaceProxyWithTarget<ISqlUtil>(coreInstance,
new MethodInterceptor<int>("SomeMethodToIntercept", 33));
// Check that calling via the proxy still delegates to existing
// generic method
Assert.AreEqual(45, proxy.SomeGenericMethod(45));
// Check that calling via the proxy returns the result we've specified
// for our intercepted method
Assert.AreEqual(33, proxy.SomeMethodToIntercept());
}
Run Code Online (Sandbox Code Playgroud)
方法拦截器看起来像这样:
public class MethodInterceptor<T> : Castle.DynamicProxy.IInterceptor {
private T _returns;
private string _methodName;
public MethodInterceptor(string methodName, T returns) {
_returns = returns;
_methodName = methodName;
}
public void Intercept(IInvocation invocation) {
if (invocation.Method.Name == _methodName) {
invocation.ReturnValue = _returns;
}
else {
invocation.Proceed();
}
}
}
Run Code Online (Sandbox Code Playgroud)
本质上,拦截器检查被调用的方法是否与您感兴趣的方法匹配,如果是,则返回存储的返回值。否则,它调用Proceed,它将方法调用委托给创建代理时提供的具体对象。
示例代码使用字符串而不是 lambda 来指定要拦截的方法,显然这可以改变(读者练习)。此外,这不是使用 Moq,因此您会丢失被拦截器替换的Setup,Returns和Verify元素,因此这可能与您所追求的内容相距太远而无法使用,但是取决于您的代码的实际情况可能是一种可行的替代方法。