views:

44

answers:

3

I was trying to setup a unit test for a private inner class, but had very little success:

namespace Stats.Model
{
  public class DailyStat
  {
    private class DailyStatKey // The one to test
    {
      private DateTime date;
      public DateTime Date 
      { 
        get { return date; }
        set { date = value.Date; }
      }

      public StatType Type { get; set; }

      public override int GetHashCode()
      {
        return Date.Year * 1000000 +
               Date.Month * 10000 +
               Date.Day * 100 +
               (int)Type;
      }

      public override bool Equals(object obj)
      {
        DailyStatKey otherKey = obj as DailyStatKey;
        if (otherKey == null)
          return false;
        return (this.Date == otherKey.Date && this.StatType == otherKey.StatType);
      }
    }
  }
}

I tried this code:

PrivateType statKeyType = new PrivateType("Stats.Model", "Stats.Model.DailyStat.DailyStatKey");

as well as

PrivateType statKeyType = new PrivateType("Stats.Model", "DailyStat.DailyStatKey");

To no avail.

The assembly's name is "Stats.Model", and to me the type name looks correct too, but I just get an exception: "System.TypeLoadException: Could not load type"

So what am I doing wrong ?

PrivateType, to the best of my knowledge, is reflection based, and I'd guess it's pretty much intended for this scenario, as you cannot have a private class directly beneath a namespace.

EDIT:

Added full implementation of DailyStatKey. What I want to test is the uniqueness of my GetHashCode method. As you can see I try to fit a date + type into a single int.

+1  A: 

Since it is private the only class that can create the instance is DailyStat itself. Unless you make it non private reflection (activator) would be your only choice if you want to create the class although that would not be a good idea as you wont be able to use it directly unless you are able to cast it to a public enough type or interface

EDIT:

Since you are trying to do this for unit testing then effectively you shouldnt test this class as it is private. You would only be able to test it through any public interface of DailyStat.

aqwert
Hmm but PrivateType does use Reflection AFAIK, so I still don't see why it's a problem.Besides what was PrivateType made for, if not this ? You can't have a private class directly in a namespace.
Steffen
You can only have private inner classes which are useful if you want to encapulate functionality only the outer class will use. You can only use new PrivateType() inside the class it is coded within. No refelction is involved. I mentioned reflection since it can be used to instatitate private classes and call private methods outside the class
aqwert
It's exactly for encapsulating functionality which is solemnly used inside the outer class. So that part is alright.I'll check whether PrivateType works within the outer class.
Steffen
Well figured out how to get the type now:var parentType = typeof(DailyStat);var keyType = parentType.NestedType("DailyKeyStat", BindingFlags.NonPublic);Works like a charm :-)
Steffen
@Steffen You can post your code as an answer.
apollodude217
@apollodude217: Hadn't thought about that, it's posted now and I'll Accept it when the 2 day limit is gone.
Steffen
A: 

You can code a public "GetDailyStatKey" method on parent class.

public class DailyStat
{
    private class DailyStatKey // The one to test 
    {
    }
    public DailyStatKey GetDailyStatKey()
    {
        return new  DailyStatKey();
    }
}

Now you can write:

DailyStat v = new DailyStat();
var x =  v.GetDailyStatKey();
x77
That is impossible because the type you are returning is invisible to the calling side. Also you can just set DailyStatKey to public if you want this.
Dykam
Like Dykam says it won't work, plus I could just go with a public inner class - which I really don't want.
Steffen
You can put an interface on the inner class which is public so you can control what is visible to the world
aqwert
@aqwert, what's the benefit of that? You can also limit exposure in the class itself.
Dykam
A: 

Found a solution myself:

var parentType = typeof(DailyStat);
var keyType = parentType.NestedType("DailyKeyStat", BindingFlags.NonPublic);

var privateKeyInstance = new PrivateObject(Activator.CreateInstance(keyType, true));

privateKeyInstance.SetProperty("Date", DateTime.Now);
privateKeyInstance.SetProperty("Type", StatType.Foo);

var hashCode = (int)privateKeyInstance.Invoke("GetHashCode", null);
Steffen