Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version
Go to top

Catel - Part 4 of n: Unit testing with Catel

, 28 Jan 2011
This article explains how to write unit tests for MVVM using Catel.
Catel-04_01-unittesting.zip
src
Catel.Articles.04 - Unit testing
Models
Properties
Settings.settings
Resources
Images
add.png
delete.png
edit.png
group.png
UI
Data
Converters
ViewModels
Windows
Catel.Articles.04 - Unit testing.Test
Models
Properties
UI
ViewModels
Catel.Articles.Base
Data
Attributes
Properties
Settings.settings
Resources
Images
CatenaLogic.png
Preview.png
Run.png
ShowCode.png
UI
Controls
Helpers
ViewModels
Windows
Catel.Core
Attributes
ClassDiagrams
DataObjectBase.cd
SavableDataObjectBase.cd
Collections
Helpers
ComponentModel
Data
Attributes
Exceptions
Interfaces
Diagnostics
Extensions
Helpers
Exceptions
Helpers
IO
Exceptions
IoC
LLBLGen
Log4net
Appender
Extensions
Helpers
MVVM
Commands
Interfaces
Exceptions
Services
EventArgs
Exceptions
Interfaces
ViewModels
Attributes
Interfaces
Properties
Reflection
Exceptions
Extensions
Helpers
Runtime
Serialization
Attributes
Helpers
Security
Cryptography
Helpers
Catel.Examples.Models
Properties
Catel.Examples.PersonApplication
Properties
Settings.settings
Resources
Images
add.png
delete.png
edit.png
group.png
UI
Data
Converters
ViewModels
Windows
Catel.Examples.Silverlight
Properties
Resources
Images
add.png
delete.png
edit.png
group.png
UI
Data
Converters
Pages
ViewModels
Windows
Catel.Examples.Silverlight.Web
Catel.Examples.Silverlight.Web.csproj.user
ClientBin
Properties
Catel.FxCop
Catel.Silverlight
Diagnostics
log4net
Core
MVVM
Commands
Services
ViewModels
Properties
Catel.Core
Catel.Windows
Reflection
Themes
Generic
Assets
Old
Windows
Controls
Data
Converters
Helpers
Helpers
Catel.Silverlight.Test
Properties
Catel.Silverlight.Test.Web
Catel.Silverlight.Test.Web.csproj.user
ClientBin
Properties
Catel.snk
Catel.Templates.WpfApplication
Properties
Settings.settings
UI
Controls
ViewModels
Windows
Catel.Templates.WpfItemTemplates
Properties
UI
Controls
ViewModels
Windows
Catel.Test
Collections
Convert
Data
Helpers
IO
MVVM
UI
ViewModels
Properties
Reflection
Runtime
Serialization
Security
Cryptography
Test References
Catel.Windows.accessor
Windows
Data
Converters
Catel.vsmdi
Catel.Windows
ClassDiagrams
ViewModelBase.cd
Collections
Extensions
Helpers
MVVM
Commands
Services
Test
UI
ViewModels
Properties
Settings.settings
Resources
Images
Add.png
ClearOutput.png
Edit.png
Error.png
Loading.gif
Preview.png
Remove.png
Save.png
TipOfTheDay.png
Warning.png
Themes
Aero
ExpressionDark
Assets
Generic
Assets
Controls
Jetpack
Assets
background.png
Old
SunnyOrange
Assets
Windows
Controls
Extensions
LinkLabel
StackGrid
Data
Converters
Helpers
Documents
Extensions
Extensions
Helpers
Input
Markup
Media
Effects
EmptyEffect
EmptyEffect.fx
EmptyEffect.ps
EmptyEffect.fx
GrayscaleEffect
GrayscaleEffect.fx
GrayscaleEffect.ps
Extensions
Imaging
Extensions
Windows
DataWindow
TipOfTheDay
Local.testsettings
Settings.StyleCop
TraceAndTestImpact.testsettings
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="SerializationHelper.cs" company="Catel development team">
//   Copyright (c) 2008 - 2011 Catel development team. All rights reserved.
// </copyright>
// <summary>
//   Class that makes serialization much easier and safer.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.Serialization;
using Catel.Properties;
using Catel.Reflection;
using log4net;

#if SILVERLIGHT
using System.IO.IsolatedStorage;
#else
using System.IO;
using System.Runtime.Serialization.Formatters;
using System.Runtime.Serialization.Formatters.Binary;
using System.Xml.Serialization;
#endif

namespace Catel.Runtime.Serialization
{
#if SILVERLIGHT
    /// <summary>
    /// Class that makes serialization much easier and safer.
    /// </summary>
    public static class SerializationHelper
    {
        #region Variables
        /// <summary>
        /// The <see cref="ILog">log</see> object.
        /// </summary>
        private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

        /// <summary>
        /// Cache for the <see cref="DataContractSerializer"/> per name.
        /// </summary>
        private static readonly Dictionary<string, DataContractSerializer> _serializers = new Dictionary<string, DataContractSerializer>();
        #endregion

        #region Methods
        /// <summary>
        /// Gets the Data Contract serializer for a specific type. This method caches serializers so the
        /// performance can be improved when a serializer is used more than once.
        /// </summary>
        /// <param name="type">The type to get the xml serializer for.</param>
        /// <param name="xmlName">Name of the property as known in XML.</param>
        /// <returns><see cref="DataContractSerializer"/> for the given type.</returns>
        public static DataContractSerializer GetDataContractSerializer(Type type, string xmlName)
        {
            return GetDataContractSerializer(type, xmlName, true);
        }

        /// <summary>
        /// Gets the Data Contract serializer for a specific type. This method caches serializers so the
        /// performance can be improved when a serializer is used more than once.
        /// </summary>
        /// <param name="type">The type to get the xml serializer for.</param>
        /// <param name="xmlName">Name of the property as known in XML.</param>
        /// <param name="loadFromCache">if set to <c>true</c>, the serializer is retrieved from the cache if possible.</param>
        /// <returns>
        /// 	<see cref="DataContractSerializer"/> for the given type.
        /// </returns>
        public static DataContractSerializer GetDataContractSerializer(Type type, string xmlName, bool loadFromCache)
        {
            return GetDataContractSerializer(type, xmlName, null, loadFromCache);
        }

        /// <summary>
        /// Gets the Data Contract serializer for a specific type. This method caches serializers so the
        /// performance can be improved when a serializer is used more than once.
        /// </summary>
        /// <param name="type">The type to get the xml serializer for.</param>
        /// <param name="xmlName">Name of the property as known in XML.</param>
        /// <param name="obj">The object to create the serializer for. When the object is not <c>null</c>, the types that are
        /// a child object of this object are added to the known types of the serializer.</param>
        /// <returns>
        /// 	<see cref="DataContractSerializer"/> for the given type.
        /// </returns>
        public static DataContractSerializer GetDataContractSerializer(Type type, string xmlName, object obj)
        {
            return GetDataContractSerializer(type, xmlName, obj, true);
        }

        /// <summary>
        /// Gets the Data Contract serializer for a specific type. This method caches serializers so the
        /// performance can be improved when a serializer is used more than once.
        /// </summary>
        /// <param name="type">The type to get the xml serializer for.</param>
        /// <param name="xmlName">Name of the property as known in XML.</param>
        /// <param name="obj">The object to create the serializer for. When the object is not <c>null</c>, the types that are
        /// a child object of this object are added to the known types of the serializer.</param>
        /// <param name="loadFromCache">if set to <c>true</c>, the serializer is retrieved from the cache if possible.</param>
        /// <returns>
        /// 	<see cref="DataContractSerializer"/> for the given type.
        /// </returns>
        public static DataContractSerializer GetDataContractSerializer(Type type, string xmlName, object obj, bool loadFromCache)
        {
            string key = string.Empty;

            if (loadFromCache)
            {
                key = string.Format("{0}|{1}", type.Name, xmlName);
                if (_serializers.ContainsKey(key))
                {
                    return _serializers[key];
                }
            }

            Log.Debug(TraceMessages.GettingKnownTypesForXmlSerialization, type.Name);

            Type[] knownTypes = GetKnownTypesForInstance(obj);

            Log.Debug(TraceMessages.FoundKnownTypesForXmlSerialization, knownTypes.Length);

            DataContractSerializer xmlSerializer = new DataContractSerializer(type, xmlName, string.Empty, knownTypes);

            if (!string.IsNullOrEmpty(key))
            {
                _serializers.Add(key, xmlSerializer);
            }

            return xmlSerializer;
        }

        /// <summary>
        /// Gets the known types for a specific object instance.
        /// </summary>
        /// <param name="obj">The object to retrieve the known types for.</param>
        /// <returns>
        /// Array of <see cref="Type"/> that are found in the object instance.
        /// </returns>
        private static Type[] GetKnownTypesForInstance(object obj)
        {
            return GetKnownTypesForInstance(obj, new List<Type>());
        }

        /// <summary>
        /// Gets the known types for a specific object instance.
        /// </summary>
        /// <param name="obj">The object to retrieve the known types for.</param>
        /// <param name="knownTypeList">The known type list.</param>
        /// <returns>
        /// Array of <see cref="Type"/> that are found in the object instance.
        /// </returns>
        /// <remarks>
        /// This code originally can be found at:
        /// http://geekswithblogs.net/SoftwareDoneRight/archive/2008/01/17/xmlserialization---solving-the-quottype-not-statically-knownquot-exception.aspx.
        /// </remarks>
        private static Type[] GetKnownTypesForInstance(object obj, List<Type> knownTypeList)
        {
            if (obj == null)
            {
                return knownTypeList.ToArray();
            }

            Type objectType = obj.GetType();
            GetKnownTypes(objectType, knownTypeList);

            if (objectType == typeof(List<KeyValuePair<string, object>>))
            {
                foreach (var keyValuePair in ((List<KeyValuePair<string, object>>)obj))
                {
                    GetKnownTypesForInstance(keyValuePair.Value, knownTypeList);
                }
            }
            else if (!objectType.FullName.StartsWith("System."))
            {
                FieldInfo[] fields = objectType.GetFields(BindingFlags.Instance | BindingFlags.Public);
                foreach (FieldInfo field in fields)
                {
                    try
                    {
                        object value = field.GetValue(obj);
                        GetKnownTypes(value == null ? field.FieldType : value.GetType(), knownTypeList);
                    }
                    catch (Exception)
                    {
                        Log.Warn(TraceMessages.FailedToGetFieldValue, field.Name, objectType.Name);
                    }
                }

                PropertyInfo[] properties = objectType.GetProperties(BindingFlags.Instance | BindingFlags.Public);
                foreach (PropertyInfo property in properties)
                {
                    try
                    {
                        object value = property.GetValue(obj, null);
                        GetKnownTypes(value == null ? property.PropertyType : value.GetType(), knownTypeList);
                    }
                    catch (Exception)
                    {
                        Log.Warn(TraceMessages.FailedToGetPropertyValue, property.Name, objectType.Name);
                    }
                }
            }

            return knownTypeList.ToArray();
        }

        /// <summary>
        /// Gets the known types inside the specific type.
        /// </summary>
        /// <param name="type">The type to retrieve the known types for.</param>
        /// <param name="knownTypeList">The known type list.</param>
        /// <returns>
        /// Array of <see cref="Type"/> that are found in the object type.
        /// </returns>
        /// <remarks>
        /// This code originally can be found at:
        /// http://geekswithblogs.net/SoftwareDoneRight/archive/2008/01/17/xmlserialization---solving-the-quottype-not-statically-knownquot-exception.aspx.
        /// </remarks>
        private static Type[] GetKnownTypes(Type type, List<Type> knownTypeList)
        {
            if (type == null)
            {
                return knownTypeList.ToArray();
            }

            // Ignore .NET or existing types
            if (type.FullName.StartsWith("System.") || knownTypeList.Contains(type))
            {
                return knownTypeList.ToArray();
            }

            knownTypeList.Add(type);

            // Fields
            FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
            foreach (FieldInfo field in fields)
            {
                GetKnownTypes(field.FieldType, knownTypeList);
            }

            // Properties
            PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
            foreach (PropertyInfo property in properties)
            {
                GetKnownTypes(property.PropertyType, knownTypeList);
            }

            // If this isn't the base type, check that as well
            if (type.BaseType != null)
            {
                GetKnownTypes(type.BaseType, knownTypeList);
            }

            return knownTypeList.ToArray();
        }

        /// <summary>
        /// Serializes the XML.
        /// </summary>
        /// <param name="isolatedStorageFileStream">The isolated storage file stream.</param>
        /// <param name="obj">The object.</param>
        /// <returns>
        /// 	<c>true</c> if the object is serialized to xml successfully; otherwise <c>false</c>.
        /// </returns>
        public static bool SerializeXml(IsolatedStorageFileStream isolatedStorageFileStream, object obj)
        {
            bool succeeded = false;

            try
            {
                if (obj != null)
                {
                    DataContractSerializer serializer = new DataContractSerializer(obj.GetType());
                    serializer.WriteObject(isolatedStorageFileStream, obj);

                    succeeded = true;
                }
            }
            catch (Exception ex)
            {
                Log.Error(ex, TraceMessages.FailedToSerializeObject);
            }

            return succeeded;
        }

        /// <summary>
        /// Deserializes the specified file name to an object.
        /// </summary>
        /// <typeparam name="T">Type of the object that is contained in the file.</typeparam>
        /// <param name="isolatedStorageFileStream">The isolated storage file stream.</param>
        /// <returns>
        /// Deserialized type or <c>null</c> if not successful.
        /// </returns>
        public static T DeserializeXml<T>(IsolatedStorageFileStream isolatedStorageFileStream) where T : class, new()
        {
            T result = null;

            try
            {
                DataContractSerializer serializer = new DataContractSerializer(typeof(T));
                result = (T)serializer.ReadObject(isolatedStorageFileStream);
            }
            catch (Exception ex)
            {
                Log.Error(ex, TraceMessages.FailedToDeserializeObject);
            }

            if (result == null)
            {
                result = new T();
            }

            return result;
        }
        #endregion
    }
#else
    /// <summary>
    /// Class that makes serialization much easier and safer.
    /// </summary>
    public static class SerializationHelper
    {
    #region Variables
        /// <summary>
        /// The <see cref="ILog">log</see> object.
        /// </summary>
        private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

        /// <summary>
        /// Cache for the <see cref="XmlSerializer"/> per name.
        /// </summary>
        private static readonly Dictionary<string, XmlSerializer> _serializers = new Dictionary<string, XmlSerializer>();
    #endregion

    #region Methods
        /// <summary>
        /// Gets the XML serializer for a specific type. This method caches serializers so the
        /// performance can be improved when a serializer is used more than once.
        /// </summary>
        /// <param name="type">The type to get the xml serializer for.</param>
        /// <param name="xmlName">Name of the property as known in XML.</param>
        /// <returns><see cref="XmlSerializer"/> for the given type.</returns>
        public static XmlSerializer GetXmlSerializer(Type type, string xmlName)
        {
            string key = string.Format("{0}|{1}", type.Name, xmlName);

            if (_serializers.ContainsKey(key))
            {
            	return _serializers[key];
            }

            XmlSerializer xmlSerializer = new XmlSerializer(type, new XmlRootAttribute(xmlName));

            _serializers.Add(key, xmlSerializer);

            return xmlSerializer;
        }

        /// <summary>
        /// Gets the <see cref="BinaryFormatter"/> for binary (de)serialization.
        /// </summary>
        /// <param name="supportRedirects">if set to <c>true</c>, redirects of types are supported. This is substantially slower.</param>
        /// <returns><see cref="BinaryFormatter"/> with the requested options.</returns>
        public static BinaryFormatter GetBinarySerializer(bool supportRedirects)
        {
            Log.Debug(TraceMessages.CreatingBinarySerializer);

            BinaryFormatter formatter = new BinaryFormatter();
            formatter.AssemblyFormat = FormatterAssemblyStyle.Simple;
            formatter.FilterLevel = TypeFilterLevel.Full;
            formatter.TypeFormat = FormatterTypeStyle.TypesWhenNeeded;

            if (supportRedirects)
            {
                formatter.Binder = new RedirectDeserializationBinder();
            }

            Log.Debug(TraceMessages.CreatedBinarySerializer);

            return formatter;
        }

        /// <summary>
        /// Retrieves a string from a SerializationInfo object.
        /// </summary>
        /// <param name="info">SerializationInfo object.</param>
        /// <param name="name">Name of the value to retrieve.</param>
        /// <param name="defaultValue">Default value when value does not exist.</param>
        /// <returns>String value.</returns>
        public static string GetString(SerializationInfo info, string name, string defaultValue)
        {
            return GetObject(info, name, defaultValue);
        }

        /// <summary>
        /// Retrieves an integer from a SerializationInfo object.
        /// </summary>
        /// <param name="info">SerializationInfo object</param>
        /// <param name="name">Name of the value to retrieve.</param>
        /// <param name="defaultValue">Default value when value does not exist.</param>
        /// <returns>Integer value.</returns>
        public static int GetInt(SerializationInfo info, string name, int defaultValue)
        {
            return GetObject(info, name, defaultValue);
        }

        /// <summary>
        /// Retrieves a boolean from a SerializationInfo object.
        /// </summary>
        /// <param name="info">SerializationInfo object.</param>
        /// <param name="name">Name of the value to retrieve.</param>
        /// <param name="defaultValue">Default value when value does not exist.</param>
        /// <returns>Boolean value.</returns>
        public static bool GetBool(SerializationInfo info, string name, bool defaultValue)
        {
            return GetObject(info, name, defaultValue);
        }

        /// <summary>
        /// Retrieves an object from a SerializationInfo object.
        /// </summary>
        /// <typeparam name="T">Type of the value to read from the serialization information.</typeparam>
        /// <param name="info">SerializationInfo object.</param>
        /// <param name="name">Name of the value to retrieve.</param>
        /// <param name="defaultValue">Default value when value does not exist.</param>
        /// <returns>object value.</returns>
        public static T GetObject<T>(SerializationInfo info, string name, T defaultValue)
        {
            Type type = typeof(T);
            object value = GetObject(info, name, type, defaultValue);
            return ((value != null) && (value is T)) ? (T)value : defaultValue;
        }

        /// <summary>
        /// Retrieves an object from a SerializationInfo object.
        /// </summary>
        /// <param name="info">SerializationInfo object.</param>
        /// <param name="name">Name of the value to retrieve.</param>
        /// <param name="type">Type of the object to retrieve.</param>
        /// <param name="defaultValue">Default value when value does not exist.</param>
        /// <returns>object value.</returns>
        public static object GetObject(SerializationInfo info, string name, Type type, object defaultValue)
        {
            try
            {
                object obj = info.GetValue(name, type);
                return obj ?? defaultValue;
            }
            catch (Exception)
            {
                return defaultValue;
            }
        }

        /// <summary>
        /// Serializes the XML.
        /// </summary>
        /// <param name="fileName">Name of the file.</param>
        /// <param name="obj">The object.</param>
        /// <returns><c>true</c> if the object is serialized to xml successfully; otherwise <c>false</c>.</returns>
        public static bool SerializeXml(string fileName, object obj)
        {
            // Failed by default
            bool succeeded = false;

            try
            {
                if (obj != null)
                {
                    Directory.CreateDirectory(IO.Path.GetParentDirectory(fileName));

                    using (FileStream fs = File.Create(fileName))
                    {
                        XmlSerializer xs = new XmlSerializer(obj.GetType());
                        xs.Serialize(fs, obj);

                        succeeded = true;
                    }
                }
            }
            catch (Exception ex)
            {
                Log.Error(ex, TraceMessages.FailedToSerializeObject);
            }

            return succeeded;
        }

        /// <summary>
        /// Deserializes the specified file name to an object.
        /// </summary>
        /// <typeparam name="T">Type of the object that is contained in the file.</typeparam>
        /// <param name="fileName">Name of the file.</param>
        /// <returns>Deserialized type or <c>null</c> if not successful.</returns>
        public static T DeserializeXml<T>(string fileName) where T : class, new()
        {
            return DeserializeXml<T>(fileName, string.Empty);
        }

        /// <summary>
        /// Deserializes the specified file name to an object. If the object cannot be serialized by the
        /// full path, the relative path is used as a last resort.
        /// </summary>
        /// <typeparam name="T">Type of the object that is contained in the file.</typeparam>
        /// <param name="fullFileName">Full name of the file.</param>
        /// <param name="relativeFileName">Name of the relative file.</param>
        /// <returns>
        /// Deserialized type or <c>null</c> if not successful.
        /// </returns>
        public static T DeserializeXml<T>(string fullFileName, string relativeFileName) where T : class, new()
        {
            T result = null;

            try
            {
                Stream stream = File.Open(fullFileName, FileMode.Open, FileAccess.Read);

                XmlSerializer xs = new XmlSerializer(typeof(T));

                result = (T)xs.Deserialize(stream);

                stream.Dispose();
            }
            catch (Exception ex)
            {
                Log.Error(ex, TraceMessages.FailedToDeserializeObject);
            }

            return result ?? new T();
        }
    #endregion
    }
#endif
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Geert van Horrik
Software Developer CatenaLogic
Netherlands Netherlands
I am Geert van Horrik, and I have studied Computer Science in the Netherlands.
 
I love to write software using .NET (especially the combination of WPF and C#). I am also the lead developer of Catel, an open-source application development framework for WPF, Silverlight, WP7 and WinRT with the focus on MVVM.
 
I have my own company since January 1st 2007, called CatenaLogic. This company develops commercial and non-commercial software.
 
To download (or buy) applications I have written, visit my website: http://www.catenalogic.com
Follow on   Twitter

| Advertise | Privacy | Mobile
Web02 | 2.8.140916.1 | Last Updated 28 Jan 2011
Article Copyright 2011 by Geert van Horrik
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid