-
-
Notifications
You must be signed in to change notification settings - Fork 75
Add support for logging via Mock #71
Description
By default when you create new Mock object - it allows to record methods being called, by placing them into "Invocations" property.
I would prefer to operate in real time and perform logging of methods being called and their call arguments.
By brief check of Moq v4 - I've noticed that this is far not simple thing to be done, as Moq does use Castle.DynamicProxy.ProxyGenerator, but it does not allow end-user of Moq to override call with it's own interceptor.
What I've came up - is maybe I could just double proxy executing - so Moq would create one proxy and I would put second proxy on top of that one.
public class LoggedMock<T>: Mock<T> where T: class
{
object logProxy;
Castle.DynamicProxy.ProxyGenerator proxygenerator;
string[] _methodsToIgnore;
public LoggedMock(
params string[] methodsToIgnore
) : base()
{
_methodsToIgnore = methodsToIgnore;
}
public override T Object
{
get
{
if (logProxy == null)
{
if (proxygenerator == null)
{
proxygenerator = new Castle.DynamicProxy.ProxyGenerator();
}
if (typeof(T).IsInterface)
{
logProxy = proxygenerator.CreateInterfaceProxyWithTarget(typeof(T), base.Object, new LoggedMockInterceptor(_methodsToIgnore));
}
else
{
logProxy = proxygenerator.CreateClassProxyWithTarget(typeof(T), base.Object, new LoggedMockInterceptor(_methodsToIgnore));
}
}
return (T)logProxy;
}
}
This did work correctly, however - then I wanted to switch from Mock<interface> to Mock<abstract class> - and then noticed that Mock does support such method as As<TInterface>(), which in a turn woul create a new copy of Mock with different generic parameter.
To support all use cases as Mock does - it would require also mimic also class AsInterface<TInterface> : LoggedMock<TInterface>
class, which drags more complexity into whole story.
Moverover - then I've noticed that Moq v5 was not based anymore on Castle core, with it's own mechanisms for logging interception.
Also if interception is done via logging - it's possible also to override existing Mock behavior and not to update Invocations at all (as it would eat more performance).
Just to illustrate what kind of logging I want to achive - this is something coded with Castle.Core / Moq v4.
public class LoggedMockInterceptor : IInterceptor
{
StringBuilder sb = new StringBuilder();
string[] _methodsToIgnore;
public LoggedMockInterceptor(string[] methodsToIgnore)
{
_methodsToIgnore = methodsToIgnore;
}
public void Intercept(IInvocation invocation)
{
string name = invocation.Method.Name;
if (_methodsToIgnore.Contains(name))
{
//invocation.Proceed();
return;
}
ParameterInfo[] parameters = invocation.Method.GetParameters();
sb.Clear();
object[] args = invocation.Arguments;
sb.Append(name);
sb.Append("(");
for (int i = 0; i < parameters.Length; i++)
{
if (i != 0)
{
sb.Append(",");
}
if (parameters[i].ParameterType == typeof(string))
{
if (args[i] == null)
{
sb.Append("null");
}
else
{
sb.Append("\"");
sb.Append(args[i].ToString());
sb.Append("\"");
}
}
else
{
sb.Append(args[i].ToString());
}
}
sb.Append(")");
LogService.GetInstance(false)._console.Info(sb.ToString());
invocation.Proceed();
}
}
Would it be possible to add into Moq v5 logging support or just to provide some code on how this could be done.