2

質問を始める前に、スタック オーバーフローに関する同様の質問がたくさんあることを認識していることを指摘しておきます。残念ながら、これらの質問のどれも、私の具体的なシナリオで適切な解決策を見つけるのに役立ちませんでした.

問題:

ロジックを含む静的ファクトリ メソッドの単体テストを作成したいと考えています。静的であっても、このメソッドを単体テストする方法を探しています。それが不可能な場合は、テスト中のクラスのより良い設計を誰かが指摘できるかもしれません。IoC の使用も検討しましたが、単体テストを考慮する利点は見られませんでした。

コード:

public class Db
{
    private XmlMapping mapping;

    public static Db<T> Create()
    {
        var mapping = XmlMapping.Create(typeOf(T).Name);
        return new Db(mapping);
    }

    private Db(XmlMapping mapping)
    {
        this.mapping = mapping;
    }
}

public class XmlMapping //class under test
{
    public static XmlMapping Create(string filename) //method under test
    {            
        try
        {
            ValidateFilename(filename);
            //deserialize xml to object of type XmlMapping
            var result = Deserialize(filename);
            if (result.IsInValid())
                throw Exception()
            return result; 
        }
        catch (Exception)
        {
            throw new DbException();
        }
    }
}

単体テストしたいメソッドCreateは、クラスXmlMapping内にあります。このメソッドは、xml ファイルをシリアル化し、XmlMapping 型のオブジェクトを生成します。シリアライズ部分のスタブを書いてみました。しかし、コンストラクターで Mapping クラスを使用してデータベース ファクトリを呼び出したくありませんでした (コンストラクター インジェクション)。

編集:

私のデータベース ファクトリは汎用です。ジェネリック タイプは、どの xml ファイルをラウドにする必要があるかを判断するために使用されます。つまり、typeOf(T) = Customer --> XmlMapping-File = Customer.xml です。

解決策 (Thx to Jeff!):

public class XmlMapping : IMapping //class under test
{
    internal static Func<Type, IMapping> DeserializeHandler { get; set; }

    static XmlMapping()
    {
        DeserializeHandler = DeserializeMappingFor;
    }

    public static IMapping Create(Type type)
    {
        try
        {
            var mapping = DeserializeHandler(type);
            if (!mapping.IsValid())
                throw new InvalidMappingException();
            return mapping;
        }
        catch (Exception ex)
        {
            throw new DataException("Failed to load mapping configuration from xml file.", ex);
        }
    }

    internal XmlMapping(IMapping mapping)
    {
        this.Query = mapping.Query;
        this.Table = mapping.Table;
        this.Entity = mapping.Entity;
        this.PropertyFieldCollection = mapping.PropertyFieldCollection;
    }

    private XmlMapping() { }
}


[TestClass]
public class MappingTests //testing class
{
    [TestMethod]
    public void Create_ValidDeserialization_ReturnsObjectInstance()
    {
        XmlMapping.DeserializeHandler = MakeFakeHandlerFor(MakeMappingStub());
        var result = XmlMapping.Create(typeof(ActivityDto));
        Assert.IsInstanceOfType(result, typeof(XmlMapping));
    }
}
4

1 に答える 1

1

逆シリアル化の呼び出しの内容を検証するために、偽のアクション ハンドラーを使用します。Func デリゲート プロパティを追加し、それをシリアライズ メソッドにデフォルト設定しましょう。XmlMapping クラスとテストは次のようになります。

public class XmlMapping //class under test
{

    static XmlMapping()
    {
        // Default the handler to the normal call to Deserialize
        DeserializeHandler = Deserialize;
    }

    public static XmlMapping Create(string filename) //method under test
    {
        //deserialize xml to object of type XmlMapping
        //preudocode:
        var result = DeserializeHandler(string.Format("{0}.xml",filename));
        //...
        return result;
    }

    // Abstract indirection function to allow you to swap out Deserialize implementations
    internal static Func<string, XmlMapping> DeserializeHandler { get; set; }

    private static XmlMapping Deserialize(string fileName)
    {
        return new XmlMapping();
    }

}

public class CreateTests {

    public void CallingDeserializeProperly()
    {

        // Arrange
        var called = false;
        Func<string, XmlMapping> fakeHandler = (string f) =>
        {
            called = true; // do your test of the input and put your result here
            return new XmlMapping();
        };

        // Act
        XmlMapping.DeserializeHandler = fakeHandler;
        var m = XmlMapping.Create("test");

        // Assert
        Assert.IsTrue(called);

    }

}
于 2012-12-06T19:58:21.220 に答える