Skip to content
This repository was archived by the owner on Jun 30, 2023. It is now read-only.
This repository was archived by the owner on Jun 30, 2023. It is now read-only.

Add support for logging via Mock #71

@tapika

Description

@tapika

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions