user6011767
user6011767

Reputation:

How to unit test DataContractSerializer?

I created my own serializer, here is the code:

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){...}

Now I would like to create a unit test. But I'm wondering how to test it?

What are common test cases should I check to be ensure that everything works fine?

Upvotes: 3

Views: 808

Answers (2)

Clay
Clay

Reputation: 5084

Don't know how much you've suffered with serialization, but here are a couple of high points (or low points, depending on your perspective):

Simple objects are trivial to serialize, and you can do pretty much anything you like to save a representation, and then reconstitute an object from that representation...you don't need a serializer to do it.

But, it turns ugly when you start having to deal with duplicate references, and cycles in your object graph (that is, when child objects that refer to their parents - and parent objects have lists of their children.) This is not uncommon.

There's a type called ObjectIdGenerator in the System.Runtime.Serialization namespace that can help you detect circularity...it helps you assign IDs to objects in your object graph, so you can spot duplicates and/or cycles.

There's no law of serialization that says your serialized data has to be organized the same way the object is organized. You can make a table-of-contents and then serialize all the objects independently, substituting tokens your deserializer will later recognize. That's what surrogates are all about.

Looking at your code - I think it's really dangerous to use a static DataContractSerializer. I can't see anywhere that it's thread-safe...and thus I wouldn't recommend it. The only real "cost" of creating a DataContractSerializer is in gathering the known types, and that you can do statically...but even then, I think having methods that add known types at runtime would be preferable to the pattern you've put forth.

Depending on the world you're in, your communications interfaces often have serializer factories that pretty much do the dirty work for you. For example, in WCF, you just send objects across the communications boundary, and WCF serializes and deserializes the objects transparently. You have a lot of control over the configuration of the serializer.

But, if you want your own serializer, that's cool...and may be called for if your world lacks such a transparent service. The biggest thing to test for is that your objects exactly match what you start with, regardless of their inner complexity...and again, the ObectIdGenerator can help with that. You can make an ObjectWalker that enumerates the inner structure of your objects.

There's an example ObjectWalker on the MSDN link I referenced, but I've messed with it for my own purposes...and this would be a useful test tool:

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;
  }
}

I hope this helps you with your efforts.

Upvotes: 0

Kevin
Kevin

Reputation: 1472

  1. Setup a test object

  2. Serialize test object

  3. Reconstitute to new instance of object

  4. Verify all data points in test object and new object match

Upvotes: 3

Related Questions