Nunit TestでC#2.0を使用しています。シリアル化する必要のあるオブジェクトがいくつかあります。これらのオブジェクトは非常に複雑です(さまざまなレベルでの継承であり、多くのオブジェクト、イベント、デリゲートが含まれています)。
オブジェクトが安全にシリアル化できることを確認するためにユニットテストを作成するにはどうすればよいですか?
私はここの仕事でいくつかのユニットテストでこれを持っています:
MyComplexObject dto = new MyComplexObject();
MemoryStream mem = new MemoryStream();
BinaryFormatter b = new BinaryFormatter();
try
{
b.Serialize(mem, dto);
}
catch (Exception ex)
{
Assert.Fail(ex.Message);
}
あなたを助けるかもしれません...多分他の方法がより良いかもしれませんが、これはうまくいきます。
一般的な方法は次のとおりです。
public static Stream Serialize(object source)
{
IFormatter formatter = new BinaryFormatter();
Stream stream = new MemoryStream();
formatter.Serialize(stream, source);
return stream;
}
public static T Deserialize<T>(Stream stream)
{
IFormatter formatter = new BinaryFormatter();
stream.Position = 0;
return (T)formatter.Deserialize(stream);
}
public static T Clone<T>(object source)
{
return Deserialize<T>(Serialize(source));
}
上記のテスト(シリアライザーがオブジェクトを受け入れることを確認する)に加えて、ラウンドトリップテストを実行する必要があります。結果を逆シリアル化して新しいオブジェクトに戻し、2つのインスタンスが同等であることを確認します。
オブジェクトを(メモリまたはディスクに)シリアル化し、逆シリアル化し、リフレクションを使用して2つを比較してから、そのオブジェクトのすべての単体テストを再度実行します(もちろんシリアル化を除く)
これは、単体テストが独自のオブジェクトを作成する代わりに、オブジェクトをターゲットとして受け入れることができることを前提としています。
これは、IsSerializableを再帰的に使用して、オブジェクトとそのすべてのプロパティがSerializableであることを確認するソリューションです。
private static void AssertThatTypeAndPropertiesAreSerializable(Type type)
{
// base case
if (type.IsValueType || type == typeof(string)) return;
Assert.IsTrue(type.IsSerializable, type + " must be marked [Serializable]");
foreach (var propertyInfo in type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
{
if (propertyInfo.PropertyType.IsGenericType)
{
foreach (var genericArgument in propertyInfo.PropertyType.GetGenericArguments())
{
if (genericArgument == type) continue; // base case for circularly referenced properties
AssertThatTypeAndPropertiesAreSerializable(genericArgument);
}
}
else if (propertyInfo.GetType() != type) // base case for circularly referenced properties
AssertThatTypeAndPropertiesAreSerializable(propertyInfo.PropertyType);
}
}
残念ながら、これを実際にテストすることはできません。この場合を想像してみてください。
[Serializable]
class Foo {
public Bar MyBar { get; set; }
}
[Serializable]
class Bar {
int x;
}
class DerivedBar : Bar {
}
public void TestSerializeFoo() {
Serialize(new Foo()); // OK
Serialize(new Foo() { MyBar = new Bar() }; // OK
Serialize(new Foo() { MyBar = new DerivedBar() }; // Boom
}
おそらく1日の少し遅い時間ですが、 FluentAssertions ライブラリを使用している場合は、XMLシリアル化、バイナリシリアル化、およびデータコントラクトシリアル化用のカスタムアサーションがあります。
theObject.Should().BeXmlSerializable();
theObject.Should().BeBinarySerializable();
theObject.Should().BeDataContractSerializable();
theObject.Should().BeBinarySerializable<MyClass>(
options => options.Excluding(s => s.SomeNonSerializableProperty));