Blog Home  Home Feed your aggregator (RSS 2.0)  
artiso Blog - PEX
Neues rund um's Thema .Net
 
 Tuesday, February 24, 2009

MSDN Webcasts 

Zusammen mit Christian Binder habe ich einen WebCast aufgenommen in dem wir PEX vorstellen. Der WebCast wird ab 12.03 online sein.

Details zur Veranstaltung: Unit-Test Generierung mit PEX [1032405246] - Microsoft Deutschland GmbH

Tuesday, February 24, 2009 9:06:26 PM (Mitteleuropäische Zeit, UTC+01:00)  #    Comments [0]    |   | 
 Friday, February 20, 2009

MSDN Webcasts

Zusammen mit Christian habe ich einen Webcast zum Thema Testing Practices aufgenommen. In dem Webcast werden zunächst verschiedene Testmethoden vorestellt und anschließend werden verschiedene Aspekte einer Teststrategie diskutiert wie z.B. Methoden zur Testfall-Ermittlung.

Der komplette Abstract lautet:

Qualität spielt in Software-Projekten eine immer größere Rolle. Ein wesentlicher Aspekt für Software-Qualität ist das Testen. Der Webcast stellt zunächst kurz die verfügbaren Testmethoden in Visual Studio vor und zeigt anschließend Aspekte einer Test-Strategie auf. Darüber hinaus wird die Integration mit dem Team Foundation Server kurz beleuchtet und es werden Methoden zur Testfallermittlung beschrieben.

Über ein Feedback zum Webcast würde ich mich freuen.

http://www.microsoft.com/germany/msdn/webcasts/library.aspx?id=1032405240

Friday, February 20, 2009 7:12:30 AM (Mitteleuropäische Zeit, UTC+01:00)  #    Comments [0]    |  |  |   | 

An einem kleinen Beispiel möchte ich kurz erläutern, wie PEX parametrisierte Unit-Tests nutzt und wie man diese nutzen kann um bestimmte Test-Szenarien abzubilden. Wir nehmen einen kleinen Codeausschnitt um uns das mal anzusehen:

   1: public class TotalSum
   2: {
   3:     private double total = 0;
   4:  
   5:     public double CalculateTotals(List<cOrderPosition> OrderPositions, double Rebate)
   6:     {
   7:         if (OrderPositions == null)
   8:             return 0;
   9:  
  10:         foreach (cOrderPosition orderPos in OrderPositions)
  11:         {
  12:             if (orderPos.Amount > 0 && orderPos.SinglePrice > 0)
  13:                 total += orderPos.Amount * orderPos.SinglePrice;                
  14:         }
  15:  
  16:         if (Rebate > 0)
  17:             total = total * (1 - Rebate);
  18:  
  19:         return total;
  20:     }
  21:  
  22:     public class cOrderPosition
  23:     {
  24:         public int ProductID { get; set; }
  25:         public double Amount { get; set; }
  26:         public double SinglePrice { get; set; }
  27:     }
  28: }

 

Auf den ersten Blick scheint da alles OK zu sein. Mal sehen was PEX daraus jetzt macht.

image

Zunächst sehen wir, dass PEX 3 Probleme mit Object Creations hat. Für den ersten Fall lassen wir PEX einfach automatisiert eine Factory erstellen indem wir auf "Accept/Edit factory" klicken. Für die Liste müssen wir ebenfalls eine Factory erstellen. Diese Factory wollen wir jetzt noch anpassen:

   1: [PexFactoryMethod(typeof(List<TotalSum.cOrderPosition>))]
   2: public static List<TotalSum.cOrderPosition> Create(int NumberOfItems)
   3: {
   4:     List<TotalSum.cOrderPosition> list = new List<TotalSum.cOrderPosition>(NumberOfItems);
   5:     if (NumberOfItems > 10)
   6:         NumberOfItems = 10;
   7:  
   8:     for (int i = 0; i < NumberOfItems; i++)
   9:     {
  10:         list.Add(new TotalSum.cOrderPosition()
  11:         {
  12:             ProductID = i + 1,
  13:             SinglePrice = new Random().NextDouble() * 10,
  14:             Amount = new Random().NextDouble() * 10
  15:         });
  16:     }
  17:        
  18:     return list;
  19: }

 

Abhängig von der Anzahl Items die als Parameter übergeben wird, wird die Liste mit entsprechend vielen Elementen befüllt. Nun erhalten wir Ergebnisse bei der Exploration.

image

Und es sind alle grün. Also alles OK? Jetzt kommt der parametrisierte Unit-Test in's Spiel. Dazu müssen wir erst mal die Test generieren. Dazu einfach alle Einträge markieren und rechts auf "Save..." klicken.

image 

   1: [TestMethod]
   2: [PexGeneratedBy(typeof(TotalSumTest))]
   3: public void CalculateTotals04()
   4: {
   5:     TotalSum totalSum;
   6:     List<TotalSum.cOrderPosition> list;
   7:     double d;
   8:     totalSum = TotalSumFactory.Create();
   9:     list = ListFactory.Create(1);
  10:     d = this.CalculateTotals(totalSum, list, 0);
  11:     Assert.AreEqual<double>(42.232177096754121, d);
  12: }

Wenn wir uns eine der generierten Testmethoden mal genauer anschauen, dann erkennen wir dass in Zeile 9 unsere ListFactory aufgerufen wird und in Zeile 10 wird eine Methode CalculateTotals aufgerufen. Bei dieser Methode handelt es sich um unseren parameterisierten Unit-Test der in der .cs-Datei abgelegt ist. Dieser parametrisierte Unit-Test nimmt Input-Werte engegen und ruft damit die eigentliche Funktion auf. Man kann den parametrisierten Unit-Test eigentlich mit einem datengetriebenen Test vergleichen, mit dem Unterschied dass die Daten nicht aus einer Datenquelle kommen sondern von den jeweiligen Testmethoden übergeben werden.

Wir können den parametrisierten Unit-Test selbst anpassen und auch Asserst einfügen. Was wir nun hier tun wollen, ist die eigentliche Test-Methode zwei mal aufzurufen und zu prüfen, ob beide Ergebnisse übereinstimmen. Bei gleichen Eingangswerten sollte dies ja der Fall sein. Der parametrisierte Unit-test sieht dann ungefähr so aus.

   1: [TestClass]
   2: [PexClass(typeof(TotalSum))]
   3: [PexAllowedExceptionFromTypeUnderTest(typeof(ArgumentException), AcceptExceptionSubtypes = true)]
   4: [PexAllowedExceptionFromTypeUnderTest(typeof(InvalidOperationException))]
   5: public partial class TotalSumTest
   6: {
   7:     [PexMethod]
   8:     public double CalculateTotals(
   9:         [PexAssumeUnderTest]TotalSum target,
  10:         List<TotalSum.cOrderPosition> OrderPositions,
  11:         double Rebate
  12:     )
  13:     {
  14:         double result = target.CalculateTotals(OrderPositions, Rebate);
  15:         double result2 = target.CalculateTotals(OrderPositions, Rebate);
  16:         Assert.AreEqual(result, result2);
  17:         return result;
  18:     }
  19: }

 

Diese Prüfung wird nun für alle Testmethoden ausgeführt. Und wie sieht das Ergebnis aus?

image

Wir erhalten nun einen Fehlerfall. Wenn man sich den Code der Test-Methode nochmals genauer anschaut, stellt man fest, dass die lokale Variable total beim erneuten Aufruf nicht zurückgesetzt wird - ein klassischer Fehler. Wenn wir die Variable in der Methode zurücksetzen, dann werden unsere Testfälle auch alle erfolgreich sein.

Somit haben wir mit Hilfe von PEX einen gängigen Fehler gefunden der in Real-World-Projekten sicher im Code selbst nicht so offensichtlich wäre.

Friday, February 20, 2009 1:20:23 AM (Mitteleuropäische Zeit, UTC+01:00)  #    Comments [1]    |  |   | 
 Thursday, February 19, 2009

Ich habe in einem früheren Post bereits einige Beispiele für den Einsatz von PEX beschrieben. ich möchte nun in loser Reihe weitere Erkenntnisse die ich beim Experimentieren mit PEX gewinne hier veröffentlichen. Heute möchte ich mich mit Overflow-Exceptions beschäftigen. Anlass war eine Diskussion mit Nico. Als erstes Beispiel wollen wir uns mal folgenden Code ansehen:

   1: public decimal Calc(decimal Value, bool Increase)
   2: {
   3:     if (Increase)
   4:         return Value+1;
   5:     else
   6:         return Value-1;
   7: }

Für eine komplette Codeabdeckung muss PEX eigentlich nur den Boolean-Parameter Increase berücksichtigen. Der Parameter Value kann eigentlich beliebig gewählt werden, auf die Code-Abdeckung hat der keinen Einfluss. Aber natürlich erkennt man dass es natürlich auch Fälle gibt in denen die Methode eine OverflowException wirft, nämlich bei value = decimal.MaxValue, Increase = true und value = decimal.MinValue, Increase = false.

Das erfreuliche ist, dass mit der aktuellen Version (0.9.40105.0) PEX zusätzlich zur Codeabdeckung auch Grenzwerte berücksichtigt:

image

Bei einem anderen Beispiel funktioniert das leider (noch) nicht.

   1: public decimal ToDecimal(double Value)
   2: {
   3:     return (decimal)Value;
   4: }

Hier wird leider keine Grenzwertbetrachtung gemacht, die ja zu einer Exception führen würde da der Wertebereich von decimal kleiner ist als der von double.

image

Thursday, February 19, 2009 8:54:48 PM (Mitteleuropäische Zeit, UTC+01:00)  #    Comments [0]    |   | 
Copyright © 2010 Thomas. All rights reserved.
DasBlog 'Portal' theme by Johnny Hughes.
Pick a theme: