如何对DataContractSerializer进行单元测试?

3
我创建了自己的序列化程序,以下是代码:

public class BackgroundJobInfoSerializer : IBackgroundJobInfoSerializer
{
    private static readonly DataContractSerializer Serializer =
        new DataContractSerializer(typeof(BackgroundJobInfo),
                                   null,
                                   int.MaxValue,
                                   false,
                                   false,
                                   new MongoDbSurrogate());

    public string Serialize(BackgroundJobInfo info)
    {
        if (info == null)
        {
            throw new ArgumentNullException("info", BackgroundJobsLocalization.BackgroundJobInfoIsNull);
        }

        var stringBuilder = new StringBuilder();
        using (var stringWriter = new StringWriter(stringBuilder, CultureInfo.InvariantCulture))
        {
            var writer = XmlWriter.Create(stringWriter);
            Serializer.WriteObject(writer, info);
            writer.Flush();
        }

        return stringBuilder.ToString();
    }

    public BackgroundJobInfo Deserialize(string info){...}

现在我想创建一个单元测试。但我在想如何测试呢?

有哪些常见的测试用例需要检查以确保一切正常工作?

2个回答

3
  1. 配置测试对象

  2. 将测试对象序列化

  3. 重新生成一个新的对象实例

  4. 验证测试对象和新对象中的所有数据点是否匹配


你编写单元测试,如果无法使它们按照你的预期工作,请发布它们并提出另一个问题。 - Kevin

0

不知道你在序列化方面遇到了多少困难,但以下是一些关键点(或低点,取决于你的观点):

简单对象序列化很容易,并且你可以采取几乎任何方法来保存一个表示,然后从该表示中重新构造对象...你并不需要一个序列化器来完成它。

但是,当你开始处理重复引用和对象图中的环路时(即子对象引用其父对象-父对象具有子对象列表),情况就变得麻烦了。这种情况并不少见。

System.Runtime.Serialization 命名空间中有一种叫做ObjectIdGenerator 的类型,可以帮助你检测循环性...它可以为对象图中的对象分配 ID,以便你可以发现重复项和/或循环。

没有序列化法则规定你的序列化数据必须按照对象组织的方式进行组织。你可以制作目录,然后独立地序列化所有对象,替换你的反序列化器后来识别的标记。这就是代理所做的事情。

看了你的代码,我认为使用静态DataContractSerializer非常危险。我没有看到它在任何地方是线程安全的...因此我不建议使用它。创建DataContractSerializer的唯一真正的“成本”在于收集已知类型,而您可以在静态情况下完成这项工作...但即使如此,我认为在运行时添加已知类型的方法比您提出的模式更可取。
根据您所处的环境,您的通信接口通常具有序列化器工厂,几乎可以为您完成所有繁琐的工作。例如,在WCF中,您只需跨通信边界发送对象,WCF会透明地对对象进行序列化和反序列化。您可以对序列化器的配置进行大量控制。
但是,如果您想要自己的序列化器,那很好...如果您的世界缺乏这样一个透明的服务,可能需要这样做。最重要的测试是确保您的对象与起始对象完全匹配,无论其内部复杂性如何...再次,ObectIdGenerator可以帮助解决这个问题。您可以制作一个ObjectWalker来枚举对象的内部结构。
MSDN链接上有一个示例ObjectWalker,但我已经为自己的目的进行了修改...这将是一个有用的测试工具:
using System;
using System.Collections;
using System.Runtime.Serialization;
using System.Reflection;

internal sealed class ObjectWalker: IEnumerable, IEnumerator
{
  object current;
  readonly Stack stack = new Stack( );
  readonly ObjectIDGenerator idGenerator = new ObjectIDGenerator( );

  public ObjectWalker( object root )
  {
    Consider( root );
  }

  IEnumerator IEnumerable.GetEnumerator( )
  {
    return this;
  }

  void IEnumerator.Reset( )
  {
    throw new NotSupportedException( );
  }

  object IEnumerator.Current { get { return current; } }


  bool IEnumerator.MoveNext( )
  {
    const BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
    if ( stack.Count == 0 ) return false;
    if ( !IsLeaf( current = stack.Pop( ) ) )
    {
      foreach ( var fieldInfo in current.GetType( ).GetFields( flags ) )
      {
        Consider( fieldInfo.GetValue( current ) );
      }
    }
    return true;
  }

  private void Consider( object toConsider )
  {
    if ( toConsider == null ) return;

    bool firstOccurrence;
    idGenerator.GetId( toConsider, out firstOccurrence );
    if ( !firstOccurrence ) return;

    if ( toConsider.GetType( ).IsArray )
    {
      foreach ( var item in ( ( Array ) toConsider ) ) Consider( item );
    }
    else
    {
      stack.Push( toConsider );
    }
  }
  bool IsLeaf( object data )
  {
    Type t = data.GetType( );
    return
      t.IsPrimitive ||
      t.IsEnum ||
      t.IsPointer ||
      data is string;
  }
}

希望这能对你的努力有所帮助。


如何创建线程安全的 private static readonly DataContractSerializer - user6011767
你可以在ReadObjectWriteObject方法周围使用锁定,但这可能会降低应用程序的可扩展性 - 因为一次只能有一个线程序列化或反序列化。 - Clay

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接