2

I am trying to test (using Moq) overloaded protected generic method in my code overloaded signatures are:

protected void AutoMap<FromType, ToType>(IList<FromType> sources
                                         , IList<ToType> destinations)

and

protected void AutoMap<FromType, ToType>(FromType from
                                         , ToType to)

To be able to access the second of these methods I am using PrivateObject and then Invoking method through reflection:

a obj_a = new a();
b obj_b = new b();
PrivateObject o = new PrivateObject(controller);
o.Invoke("AutoMap"
         , new Type[] { typeof(a), typeof(b) }
         , new Object[] { obj_a, obj_b }
         , new Type[] { typeof(a), typeof(b) });

When I run the test I got System.Reflection.AmbiguousMatchException saying Ambiguous match found.

Can anyone advise me how to do that correctly ? Cheers K.

Karol
  • 53
  • 8

2 Answers2

2

In general, you shouldn't be testing protected methods. Only public methods should be tested. Protected methods should be treated as private from unit testing perspective.

You should test your implementing class, and see that it works as expected. If this class uses a protected/private method (s) to do it's job - this is implementation detail, and implementation can change any time w/o changing the behavior of the class (think refactoring).

Assuming you have:

public abstract class Base
{
   public virtual int GetResult(int data)
   {
       var z = GetMoreData();
       return data*z;
   }
   protected int GetMoreData()
   {
      ///something
      return something;
    }
}

public class MyRealImplementation : Base
{
    public override int GetResult(int data)
    {
       //wahtever
    }
}

There is no way (or no point) to test the protected method. It is not known that the overridden GetResult will ever use it (it may, or it may not).

The only think to test is if GetResult returns the expected result, not how it does that.

In the particular case, it's better if you abstract behind an interface the mapping functionality, so you can mock it.

public interface IMapper
{
    AutoMap<FromType, ToType>(IList<FromType> sources, IList<ToType> destinations);
    oid AutoMap<FromType, ToType>(FromType from, ToType to);
}

The inject that interface in your PrivateObject and use it instead of protected method. With that you will:

  1. have your classes/methods following the single responsibility principle
  2. test the mappings independently
  3. use mocks to test your private class

In general - every time it is hard to write a unit test, usually this means that the class/method do too much.

Sunny Milenov
  • 21,990
  • 6
  • 80
  • 106
1

While I agree with Sunny's answer regarding the folly of testing protected methods, this problem interested me so I tried figuring out what was going wrong here.

The problem is a limitation of the PrivateObject class, which is basically just a wrapper over some reflection methods. This is a known limitation, see example questions here and here.

If you really want to test a method like this, you need to use these reflection methods yourself.

//this is ugly, but the quickest way I could filter down to the overload that 
//doesn't take in the IList interface arguments
var method = typeof (YOUR_CONTROLLER_CLASS).GetMethods(
             BindingFlags.Instance | BindingFlags.NonPublic)
            .First(m => m.IsGenericMethod 
                && m.Name == "AutoMap" 
                && !m.GetParameters()[0].ParameterType.IsInterface);
var genericMethod = method.MakeGenericMethod(new[] {typeof (a), typeof (b)});
genericMethod.Invoke(controller, new object[]{ obj_a, obj_b });

Hope that helps.

Community
  • 1
  • 1
Sven Grosen
  • 5,616
  • 3
  • 30
  • 52