フェイクにモックを加えるクラス

これを毎回実装する手間を省くために、RealProxy 使って WrappingMock なんてクラスを作ってみた。

using System;
using System.Reflection;
using System.Runtime.Remoting.Messaging;
using System.Runtime.Remoting.Proxies;
using NUnit.Mocks;

public sealed class WrappingMock<T> : Mock
{
    public WrappingMock(T rawInstance)
    {
        if (rawInstance == null)
        {
            throw new ArgumentNullException("rawInstance");
        }
        this._mockProxy = new MockProxy(rawInstance, this);
    }

    private readonly MockProxy _mockProxy;

    public T MockInstance
    {
        get
        {
            return this._mockProxy.GetTransparentProxy();
        }
    }

    private sealed class MockProxy : RealProxy
    {
        private readonly T _targetInstance;

        private readonly ICallHandler _callHandler;

        public T TargetInstance
        {
            get
            {
                return this._targetInstance;
            }
        }

        public MockProxy(T targetInstance, ICallHandler callHandler)
            : base(typeof(T))
        {
            this._targetInstance = targetInstance;
            this._callHandler = callHandler;
        }

        public override IMessage Invoke(IMessage msg)
        {
            IMethodCallMessage methodCallMessage = (IMethodCallMessage)msg;
            MethodBase targetMethod = methodCallMessage.MethodBase;
            object[] args = methodCallMessage.Args;

            ReturnMessage returnMessage;
            try
            {
                object result = this._callHandler.Call(targetMethod.Name, args) ?? targetMethod.Invoke(this.TargetInstance, args);
                returnMessage = new ReturnMessage(result, args, args.Length, methodCallMessage.LogicalCallContext, methodCallMessage);
            }
            catch (Exception ex)
            {
                ex = (ex is TargetInvocationException) ? ex.InnerException : ex;
                returnMessage = new ReturnMessage(ex, methodCallMessage);
            }
            return returnMessage;
        }

        public new T GetTransparentProxy()
        {
            return (T)base.GetTransparentProxy();
        }
    }
}

前回と同じく、こんな IHoge インターフェイスがあって

public interface IHoge
{
    bool Piyo();
}

前回と同じく、こんな Fuga クラスが使用するとする。

public class Fuga
{
    private IHoge _hoge;
    public Fuga(IHoge hoge)
    {
        this._hoge = hoge;
    }
    public bool HogePiyo()
    {
        return this._hoge.Piyo();
    }
}

今回はフェイクを単純に実装できる。

public class HogeFake : IHoge
{
    public bool Piyo()
    {
        return true;
    }
}

で、こんな風に使用できる。

[TestFixture]
public class TestFixture1
{
    [Test]
    public void Test1()
    {
        WrappingMock<IHoge> hogeMockery = 
            new WrappingMock<IHoge>(new HogeFake());
        hogeMockery.Strict = true;
        hogeMockery.Expect("Piyo");

        IHoge hogeMock = hogeMockery.MockInstance;
        Fuga f = new Fuga(hogeMock);
        bool b = f.HogePiyo();

        Assert.That(b, Is.True);
        hogeMockery.Verify();
    }
    [Test]
    public void Test2()
    {
        WrappingMock<IHoge> hogeMockery = new WrappingMock<IHoge>(new HogeFake());
        hogeMockery.Strict = true;
        hogeMockery.ExpectAndReturn("Piyo", false);

        IHoge hogeMock = hogeMockery.MockInstance;
        Fuga f = new Fuga(hogeMock);
        bool b = f.HogePiyo();

        Assert.That(b, Is.False);
        hogeMockery.Verify();
    }
    [Test]
    [ExpectedException(typeof(InvalidOperationException))]
    public void Test3()
    {
        WrappingMock<IHoge> hogeMockery = new WrappingMock<IHoge>(new HogeFake());
        hogeMockery.Strict = true;
        hogeMockery.ExpectAndThrow("Piyo", new InvalidOperationException());

        IHoge hogeMock = hogeMockery.MockInstance;
        Fuga f = new Fuga(hogeMock);
        f.HogePiyo();
    }
}

見ての通り、DynamicMock と似たような使い方ができる。