internal を利用してテスト時にモックと差し替える方法

internal を利用すれば、DI コンテナを使わなくても、呼び出すクラスをモックに差し替えられるんじゃないだろうか。

例えば次のクラス。

public class FooService
{
    public Foo FindFoo(int id)
    {
        FooDataAccsss dac = new FooDataAccess();
        return dac.Find(id);
    }
    public void CreateFoo(Foo data)
    {
        FooDataAccess dac = new FooDataAccess();
        return dac.Create(data)
    }
}

FooDataAccess のインスタンスを生成している箇所を、次のように書き変えてみる。

public class FooService
{
    internal Func<FooDataAccess> CreateDataAccess;
    public FooService()
    {
        CreateDataAccess = () => new FooDataAccess();
    }
    public Foo FindFoo(int id)
    {
        return CreateDataAccess().Find(id);
    }
    public void CreateFoo(Foo data)
    {
        CreateDataAccess().Create(data);
    }
}

こうしておけば、テスト内で FooDataAccess の生成処理を差し替えることができる。テストプロジェクトのアセンブリを InternalVisibleTo に指定して、internal メンバへのアクセスを許可する必要があるけど。

FooService service = new FooService();
service.CreateDataAccess = () => {
    Mock<FooDataAccess> mock = new mock<FooDataAccess>();
    mock.Setup(m => m.Find(10)).Returns(new Foo());
    return mock.Object;
};

ちなみに、上記の例では Moq を使っている。他のモックライブラリを使う場合は、インタフェースを用意するといった工夫が必要。

試してみた感想としては、単体テストはしやすくなるけど、美しくないなぁ。自分は CreateXXX みたいな、クラス内部だけで利用する生成メソッドをよく書くので、面倒だとは思わないけれど。