Tuesday, June 08, 2004

To or not to unit test provate methods?


PrivateObject Unit Tester



AndrewSeven was kind enough to direct me to a blog by James Newkirk briefly introducing the PrivateObject class available at part of the VS Team System framework.


I was intrigued enough to start investigating and ended up implementing what I think is a close interpretation of this class, shown below.


The primary interface is the constructor which accepts an assembly qualified type name as the first parameter (used by Type.GetType()).  The second parameter is optional and is passed as the arguments to instantiate the type; these arguments determine the constructor signature to choose.


As documented in the Type.GetType() method, the qualifiedTypeName should be of the form MyNamespace.MyClass,MyAssemblyBaseName.


Here's the code:

using System;
using System.Reflection;
using System.Security.Permissions;

 

#region .
/// <summary>
/// This utility class uses reflection to wrap an instance of a

/// class to gain access to non-public members of that class.

/// This is an internal utility class used for unit testing.
/// </summary>
#endregion
public class PrivateObject
{
    private const BindingFlags bindingFlags

= BindingFlags.Instance | BindingFlags.NonPublic;


    private Type type;                  // type of class to manage
    private object instance;            // managed instance of type
    private ReflectionPermission perm;  // for non-public members


 

    #region .
    /// <summary>
    /// Initializes a new instance wrapping a new instance of the

/// target type. One PrivateObject manages exactly one instance

/// of a target type.
    /// </summary>
    /// <param name="qualifiedTypeName">The qualified name of the type.

/// This should include the full assembly qualified name including

/// the namespace, for example "MyNamespace.MyType,MyAssemblyBaseName"

/// where MyNamespace is the dotted-notation namespace, MyType is the

/// name of the type and MyAssemblyBaseName is the base name of the

/// assembly containing the type.
    /// </param>
    /// <param name="args">An optional array of parameters to pass to

/// the constructor. If this argument is not specified then the

/// default constructor is used. Otherwise, a constructor that

/// matches the number and type of parameters is used.
    /// </param>
    #endregion
    public
PrivateObject (string qualifiedTypeName, params object[] args)
    {
        perm = new ReflectionPermission(PermissionState.Unrestricted);
        perm.Demand();


        type = Type.GetType(qualifiedTypeName);


        Type[] types = new Type[args.Length];
        for (int i=0; i < args.Length; i++)
        {
            types[i] = args[i].GetType();
        }


        instance = type.GetConstructor(bindingFlags,null,types,null).Invoke(args);
    }


 

    #region .
    /// <summary>
    /// Gets the instance of the managed object.
    /// </summary>
    #endregion
    public object Instance
    {
        get { return instance; }
    }


 

    #region .
    /// <summary>
    /// Gets the value of a non-public field (member variable) of the

/// managed type.
    /// </summary>
    /// <param name="name">The name of the non-public field to

/// interrogate</param>
    /// <returns>A value whose type is specific to the field.</returns>
    #endregion
    public object
GetField (string name)
    {
        return type.GetField(name,bindingFlags).GetValue(instance);
    }



    #region .
    /// <summary>
    /// Gets the value of a non-public property of the managed type.
    /// </summary>
    /// <param name="name">The name of the non-public property to

/// interrogate.</param>
    /// <returns>A value whose type is specific to the property.</returns>
    #endregion
    public object
GetProperty (string name)
    {
        return type.GetProperty(name,bindingFlags).GetValue(instance,null);
    }


 

    #region .
    /// <summary>
    /// Invokes the non-public method of the managed type.
    /// </summary>
    /// <param name="name">The name of the non-public method to invoke.</param>
    /// <param name="args">And optional array of typed parameters to pass to the
    /// method.  If this argument is not specified then the routine searches
    /// for a method with a signature that contains not parameters.  Otherwise,
    /// the procedure searches for a method with the number and type of parameters
    /// specified.
    /// </param>
    /// <returns>A value who type is specific to the invoked method.</returns>
    #endregion
    public object Invoke (string name, params object[] args)
    {
        Type[] types = new Type[args.Length];
        for (int i=0; i < args.Length; i++)
        {
            types[i] = args[i].GetType();
        }


        return type.GetMethod(name,bindingFlags,null,types,null).Invoke(instance,args);
    }


 

    #region .
    /// <summary>
    /// Sets the value of a non-public field (member variable) of the managed type.
    /// </summary>
    /// <param name="name">The name of the non-public field to modify.</param>
    /// <param name="val">A value whose type is specific to the field.</param>
    #endregion
    public void
SetField (string name, object val)
    {
        type.GetField(name,bindingFlags).SetValue(instance,val);
    }


 

    #region .
    /// <summary>
    /// Sets the value of a non-public property of the managed type.
    /// </summary>
    /// <param name="name">The name of the non-public property to modify.</param>
    /// <param name="val">A value whose type is specific to the property.</param>
    #endregion
    public void SetProperty (string name, object val)
    {
        type.GetProperty(name,bindingFlags).SetValue(instance,val,null);
    }
}


 

Sunday, June 06, 2004

Marked:


.NET Tool/Control vendors/creators: new directory site: www.developerfood.com!




A good friend of mine, Scott Wallace, has created a new .NET tool/control directory site: http://www.developerfood.com! If you are a tool / control vendor or a freeware/open source tool/control programmer, feel free to add your tool/control to the directory. The site is free, doesn't sell the controls / tools it lists nor is it affiliated with a tool/control vendor.



I think it is a great initiative and it takes just a few minutes to get your tool/control enlisted. One for the bookmarks!

Wednesday, June 02, 2004

SQL Server Developer Center:


SQL Server Developer Center



This is nice:



Microsoft has launched a SQL Server Developer Center. [by way of Scott Swigart/Early Adopter]


Looking forward to some very good resources from this site.

Windows Media Play 10 Public Beta is available:


href="http://www.microsoft.com/windows/windowsmedia/mp10/default.aspx#download">http://www.microsoft.com/windows/windowsmedia/mp10/default.aspx#download