Bei Unit Tests ist es manchmal notwendig auch auf private Member zuzugreifen. Dies kann beispielsweise geschehen wenn man ein privates Feld oder eine Property mit einem anderen Wert belegen will oder wenn eine private Methode mehrfach Fehler lieferte und man sie deshalb separat von ihren öffentlichen Aufrufern testen will. Microsoft bietet für MS Tests deshalb die Klasse PrivateObject an, welche den Zugriff auf jedes private Element zulässt. Naja, auf fast jedes…

Zunächst betrachten wir ersteinmal das Szenario. In einem Projekt wurde kein IoC eingesetzt. Um dennoch die Funktionalität einer Methode testen zu können hat man sich entschieden alle Elemente die später gemockt werden sollen in protected Properties auszulagern:

public class A
{
   protected DataProvider dataProvider
   {
      get;
      set;
   }
   // weitere Methoden
}

Testet man nun Klasse A kann man mit PrivateObject einen anderen DataProvider setzen und dann beliebige Daten zurück geben. Die Grundlage für einen Unit Test ist somit gegeben („Teste nur das was auch wirklich zu testen ist“).

A a = new A();
var accessor = new PrivateObject(a);
accessor.SetFieldOrProperty("dataProvider", dataProviderStub);
a.DoSomething();

Wie man an der Deklaration von DoSomething und der DataProvider Property aber bereits sieht, gibt es Klassen die von A ableiten. Damit diese den DataProvider nicht ungefragt überschreiben wurde dessen setter private gesetzt. B macht nun etwas anderes, nutzt aber den gleichen DataProvider. Daher ist die Überlegung nicht abwegig hier wieder per PrivateObject einen Stub anzusetzen der dann die gewünschten Daten zurück gibt.

public class A
{
   protected DataProvider dataProvider
   {
      get;
      private set;
   }
   // weitere Methoden
}
public class B : A
{
   // weitere Methoden
}

 

B b = new B();
var accessor = new PrivateObject(b);
accessor.SetFieldOrProperty("dataProvider", dataProviderStub);
b.DoSomething();

Leider funktioniert das nicht. Stattdessen fliegt beim Setzen der Property eine MissingMethodException. Der Grund dafür ist, dass wir sie in A auf private gesetzt haben. Demnach kann sie für B nicht gesetzt werden, es ist ja verboten. PrivateObject hebelt demnach nicht die typischen Sichtbarkeiten aus. Aus diesem Grund kann es den gewünschten Setter nicht „sehen“ und schmeißt, berechtigter Weise, eine MissingMethodException.

Entfernt man demnach das Private beim Setter, ist die Welt wieder in Ordnung. Leider können die abgeleiteten Klassen dann aber auch die entsprechenden Werte überschreiben, was evtl. zu anderen Problemen führt.


Kick It auf dotnet-kicks.de