开箱即用的 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;
}
}
然后您可以进行以下测试:
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());
}
方法拦截器如下所示:
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();
}
}
}
本质上,拦截器检查正在调用的方法是否与您感兴趣的方法匹配,如果是,则返回存储的返回值。否则,它会调用Proceed
,它将方法调用委托给创建代理时提供的具体对象。
示例代码使用字符串而不是 lambda 来指定要拦截的方法,显然这可以更改(供读者练习)。另外,这没有使用起订量,所以你会失去Setup
, Returns
and Verify
元素,它们被拦截器取代,因此这可能与您想要的有用的东西相距太远,但是根据您的代码的实际情况,它可能是一种可行的替代方法。