Privaten Code testen mit MSTest

Gerade wenn man die Codequalität in Brownfieldanwendungen durch UnitTests erhöhen möchte, steht man oftmals vor dem Problem das man privaten Code testen muss. Vor einigen Tagen hat Ralf in seinem Artikel gezeigt wie man dies mit den dynamischen Features von C# machen kann.

Dies kann man mit MSTest allerdings noch einfacher haben.

MSTest stellt mit der Klasse PrivateObject einen Wrapper um die benötigte Reflectionlogik bereit. Die Klasse PrivateObject stellt entsprechende Methode bereit um private Methoden, private Properties usw. aufzurufen.

Wie PrivateObject funktioniert, sieht man am einfachsten am Beispiel einer FizzBuzz Implementierung

public class FizzBuzzService
{
        private const String Fizz = "Fizz";
        private const String Buzz = "Buzz";
        public String StringFor(int number)
        {
            if (IsFizz(number) && IsBuzz(number))
                return Fizz + Buzz;
            if (IsFizz(number))
                return Fizz;
            if (IsBuzz(number))
                return Buzz;
            return number.ToString();
        }
 
        private bool IsFizz(int number)
        {
            return number % 3 == 0;
        }
 
        private bool IsBuzz(int number)
        {
            return number % 5 == 0;
        }
}

Für dieses Beispiel möchte ich einfach die Implementierung der UnitTests für die Methoden IsFizz und IsBuzz, die einzigen privaten Methoden in diesem Kontext, hier aufführen.

[TestClass()] 
public class FizzBuzzTest 
{ 
    privateTestContext testContextInstance; 
        
    public TestContext TestContext 
    { 
        get 
       { 
            return testContextInstance; 
        } 
        set 
       { 
            testContextInstance = value; 
        } 
    } 
 
   [TestMethod()] 
    public void Is_Fizz_Should_Return_True_If_Three_Is_Passed() 
    { 
        bool expected = true; 
        var sut = new PrivateObject(typeof (FizzBuzzService)); 
        var actual = sut.Invoke("IsFizz",new object[]{3}); 
        Assert.AreEqual(expected, actual); 
    } 
 
    [TestMethod] 
    public void Is_Buzz_Should_Return_True_If_Five_Is_Passed() 
    { 
        bool expected = true; 
        var sut = new PrivateObject(typeof (FizzBuzzService)); 
        var actual = sut.Invoke("IsBuzz", new object[] {5}); 
        Assert.AreEqual(expected,actual); 
    } 
} 

Der Einstieg ins Testen der privaten Methoden geschieht jeweils mit der Erstellung des SystemUnderTest oder kurz SUT. Das SUT erstelle ich in meinem Beispiel durch die einfachste Überladung des PrivateObject Konstruktors

var sut = new PrivateObject(typeof (FizzBuzzService)); 

Als Alternative kann man auch eine vorhandene Objektinstanz in den Konstruktor geben um den PrivateObject Wrapper zu verwenden:

FizzBuzzService service = new FizzBuzzService();
var sut = new PrivateObject(service);

Private Methoden des SUTs können sehr einfach durch die Methode Invoke realisiert werden. Enstprechend der Methodensignatur müssen die Parameter der Zielmethode in Form eines Object-Arrays übergeben werden. Der Rückgabewert der Methode ist immer ein Object, daher bietet es sich an, hier auf var zu schwenken und die Typisierung dem Compiler zu überlassen.

var actual = sut.Invoke("IsBuzz", new object[] {5});

durch die Invoke Methode durchgeführt. Gerade im Brownfield Umfeld finde ich dieses Feature sehr sinnvoll und spart einem Entwickler in vielen dieser Fälle sehr viel Zeit.

Beim Ausführen der Tests sieht man, dass alles schön grün ist (falls die Methode macht was sie soll ;D)

TestPrivateCode

Demnach viel Spaß beim Testen des PrivateCodes ;)

Die gesamte API der Klasse PrivateObject kann man der MSDN entnehmen.

Technorati-Tags: ,,
Published Freitag, 5. November 2010 21:25 von ThorstenHans
Abgelegt unter: , ,

Kommentare

# Privaten Code testen mit MSTest – .NET rocks

Donnerstag, 11. November 2010 22:12 von Privaten Code testen mit MSTest – .NET rocks

Ping Antwort von  Privaten Code testen mit MSTest – .NET rocks

Kommentar abgeben

(verpflichtend) 
(verpflichtend) 
(optional)
(verpflichtend)