Skip to content

DRNJ

Light at the end of the Technology Tunnel

  • Home
  • About
  • Contact
DRNJ

Month: February 2019

Unit Testing Protected and Private Methods

February 6, 2019

The problem

You write a class in true SOLID tradition, and then you want to write the unit tests (or you do true TDD and do the reverse).

All well and good until you want to test some “internal” protected or private methods.

Purists would say you only need to test the external public interface. My response is that this is rubbish, you should test bite-sized pieces of fuctionality, especially if you are writing base classes (and no I don’t want to get into a discussion of programming against interfaces only and not using inheritance)

The Solution

After some googling and manipulation I discovered the Microsoft PrivateObject class (in the Microsoft.VisualStudio.QualityTools.UnitTestFramework assembly). This allows the “wrapping” of the class to be tested and then it’s protected or private methods can be invoked.

I discovered a wrapper to this PrivateObject class which makes unit testing straightforward e.g. if class ClassToTest has a protected method with signature ProctedMethod(int,string) then it can be invoked for testing as:

var x = new ClasstoTest();
dynamic target = new DynamicAccessor(x, typeof(ClassToTest));
target.ProtectedMethod(1,"abcd");

 

This has worked fine and dandy until trying to test a class with generic methods. I found some information on StackOverflow (this article, and this one)  I modified the class to use reflection and it works for most generic methods (it’s still not perfect).

The Code

 

  /// <summary>
    /// DynamicAccessor
    ///
    /// Class to be utilised in Unit Tests to allow protected and private
    /// members and properties to be called from external tests
    ///
    /// Not perfect and based on someone else's hard word along with
    /// a bit of hot-rod magic
    /// 
    /// </summary>
    public class DynamicAccessor : DynamicObject
    {
        public PrivateObject privateObject;
        private object parentObject;

        public DynamicAccessor(object d)
        {
            this.privateObject = new PrivateObject(d);
        }

        public DynamicAccessor(object d, Type t)
        {
            this.parentObject = d;
            this.privateObject = new PrivateObject(d, new PrivateType(t));
        }


        /// <summary>
        /// The try invoke member.
        ///
        /// Doesn't work as is with Generic<T> methods
        ///
        /// so use a combination of
        /// https://stackoverflow.com/questions/5492373/get-generic-type-of-call-to-method-in-dynamic-object
        /// to get type params and
        ///  https://stackoverflow.com/questions/42006662/testing-private-static-generic-methods-in-c-sharp
        /// to invoke
        ///
        /// Hot Rod Programming (DJ)
        /// 
        /// </summary>
        /// <param name="binder">
        /// The binder.
        /// </param>
        /// <param name="args">
        /// The args.
        /// </param>
        /// <param name="result">
        /// The result.
        /// </param>
        /// <returns>
        /// The <see cref="bool"/>.
        /// </returns>
        public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
        {
            try
            {
                // https://stackoverflow.com/questions/5492373/get-generic-type-of-call-to-method-in-dynamic-object
                var csharpBinder = binder.GetType().GetInterface("Microsoft.CSharp.RuntimeBinder.ICSharpInvokeOrInvokeMemberBinder");
                var typeArgs = (csharpBinder.GetProperty("TypeArguments").GetValue(binder, null) as IList<Type>);

                //------------------------------------
                // if typeargs then a generic method |
                //------------------------------------
                if (typeArgs.Any())
                {
                    //----------------------------------------------------------------------------------------------------
                    // Use reflection to get to method                                                                   |
                    // https://stackoverflow.com/questions/42006662/testing-private-static-generic-methods-in-c-sharp    |
                    //----------------------------------------------------------------------------------------------------


                    MethodInfo fooMethod = this.parentObject.GetType().GetMethod(binder.Name, BindingFlags.NonPublic | BindingFlags.Instance);
                    if (fooMethod == null)
                    {
                        result = null;
                        return false;
                    }
                    //-------------------------------------------
                    // Turn Into Generic and invoke with params |
                    //-------------------------------------------
                    MethodInfo genericFooMethod = fooMethod.MakeGenericMethod(typeArgs.ToArray());
                    result = genericFooMethod.Invoke(this.parentObject, args);
                }
                else
                {
                    //-----------------------------
                    // Non Generic so just invoke |
                    //-----------------------------
                    result = privateObject.Invoke(binder.Name, args);
                }

                return true;
            }
            catch (MissingMethodException)
            {
                result = null;
                return false;
            }
        }

        public void SetProperty(string propName, object o)
        {
            this.privateObject.SetProperty(propName, o);
        }
    }

 

DotNet

Recent Posts

  • AutoMapper and “Could not load type ‘SqlGuidCaster'” Error
  • OpenVPN on Docker and the Strange Error Message Saga
  • Docker CLI and Compose Information Message
  • Docker Containers and Azure – An Introduction
  • Serilog in .Net Core 6

Recent Comments

    Archives

    • April 2025
    • December 2024
    • April 2024
    • September 2022
    • November 2021
    • June 2021
    • March 2021
    • July 2020
    • April 2020
    • November 2019
    • September 2019
    • July 2019
    • May 2019
    • February 2019
    • July 2018
    • June 2018

    Categories

    • .NET Core
    • Azure
    • Docker
    • DotNet
    • Security
    • Uncategorized
    • WebAPI
    • Windows

    Meta

    • Log in
    • Entries feed
    • Comments feed
    • WordPress.org

    Idealist by NewMediaThemes