Archiv für den Monat Mai 2011

Factory Method Design Pattern

Ich konnte kürzlich das Factory Method Pattern schön im Kontext der Web/SharePoint Entwicklung einsetzen, als eine Klasse ihre SPWeb Abhängigkeit per Constructor Injection reingereicht bekommen sollte. Hier die zwei Samples dazu:

   1: public class Sample1

   2: {

   3:     private SPWeb _web;

   4:     public Sample1(SPWeb web)

   5:     {

   6:         _web = web;

   7:     }

   8: }

   9:  

  10: public class Sample2

  11: {

  12:     private Func<SPWeb> _web;

  13:     public Sample1(Func<SPWeb> webFactory)

  14:     {

  15:         _web = webFactory;

  16:     }

  17: }

Während in Sample1 immer das gleiche Objekt für Aufrufe auf dem SPWeb-Kontext verwendet wird, ist es bei Sample2 so, dass man jedes Mal ein neues SPWeb Objekt erhält und darauf seine Abfragen ausführt.

Das hat in diesem Szenario primär den Vorteil, dass ich in Sample 2 mit Using arbeiten könnte, sodass die Methode, die das SPWeb Objekt verwenden will, sich selbstständig um das disposen kümmern kann. Das ist gerade bei Web/SharePoint Anwendungen wichtig, da Microsoft hier kaum Interfaces anbietet, die man bei UnitTests verwenden kann. Dementsprechend muss man sich eigene Interfaces und Wrapper-Implementierungen bauen, die man mocken kann. Allerdings würde das bedeuten, dass das aufrufende Objekt des Fakes sich um den Lifecycle der Fake-Abhängigkeiten kümmern muss, was sehr lästig ist. Für die konkrete Implementierung sähe das natürlich nicht anders aus…

Auf folgender Seite findet ihr noch eine sinnvolle Anwendungsmöglichkeit: http://www.dofactory.com/Patterns/PatternFactory.aspx#_self2

Wikipedia kann euch weitere Details zu dem Pattern nennen.

Visual Studio 2010 from scratch

PC ist neu aufgesetzt, Visual Studio frisch installiert. Wenn man mit dem ersten Projekt anfängt, merkt man schnell: Da fehlt doch etwas!

Hier meine Steps, wie ich vorgehe, um für eine Grundausstattung zu sorgen:

  • ReSharper downloaden und installieren
  • ReSharper User Settings von meiner zweiten Kiste bzw. aus dem Backup zurückspielen: Dazu einfach in das Verzeichnis %appdata%\Roaming\JetBrains\ReSharper\v5.1\vs10.0 (Achtung: Gilt nur für Resharper Version 5.1.*) gehen und dort die UserSettings.xml ersetzen
  • NuGet Package Manager als Extension installieren (eine der wohl coolsten Extensions überhaupt)
  • Und gleich dazu noch die Productivity Power Tools

 

Jetzt kann es los gehen mit dem ersten Projekt. Projekt ist angelegt und wieder kommt es einem: Da fehlt doch etwas!

Über Tools – Library Package Manager – Package Manager Console starten und folgende Befehle eintippen:

  • Install-Package Machine.Specifications-Signed
  • Install-Package FakeItEasy
  • Install-Package Castle.Windsor

Wer nicht bereits entsprechende Live Templates für MSpec in ReSharper in der UserSettings.xml hatte, kann diese sich von Alex Groß herunterladen:

MSpec Resharper Templates

Das sind für mich die rudimentären Tools, die ich inzwischen in jedem Projekt benötige. Auf nice-to-have Extensions will ich absichtlich nicht eingehen, da das dann doch ein sehr weites Feld ist.

How to test static code: Sample with the DateTime-Object

Static Methods are everywhere, especially if the code is from Microsoft. How you work this?

Make an interface with an instance method/member, make a class that implementes the interface an within this instance you can call the static stuff.

Example: The DateTime-object has the member Now, so if you call DateTime.Now, you will get the current time. Unfortunately, can’t test time relevant actions like how long took a print job this way.

First: Declare the interface (VB.NET Code):

   1: Public Interface IClock

   2:     ReadOnly Property Now As DateTime

   3: End Interface

 

Second: Implement it (C# Code):

   1: public class SystemClock : IClock

   2: {

   3:     public DateTime Now

   4:     {

   5:         get { return DateTime.Now; }

   6:     }

   7: }

 

With Fake It Easy you can create a mock like this:

   1: A.Fake<IClock>();

 

Important: Don’t forget to register this in your IoC-Container respectively inject the SystemClock-Object into your object/method that used the DateTime.Now earlier.

Fake It Easy–don’t forget about limiting factors

Today i want to talk about a feature of Fake It Easy that you can need in many cases:

If you have a method that retrieves you job ids that can be processed by the calling method (e.g. a print service with a job id provider that observes the database for new jobs). In my case, if there is no job, the method returns –1. But there are some circumstances in which my providers also returns –1 for example when i stopped my provider (in case of multithreading) or when an expected exception occurs (when the database is down).

So i don’t want to talk about how clean this code is if it acts like the way i told you, instead i want to make clear that this are two kinds of tests you’ve got to write:

  • Case 1: There is no next job in the database
  • Case 2: Any other cases with the same return value but another meaning/behaviour inside your method

The extension methods ‘MustHaveHappened’ and ‘MustNotHaveHappened’ are your friends:

   1: It should_not_query_for_the_next_print_job_id =

   2:     () => A

   3:             .CallTo( () => _dbConversation.Query(A<NextWaitingPrintJob>

   4:             .That.Not.IsNull()))

   5:             .MustNotHaveHappened();

   6:  

   7: It should_return_the_default_job_id = () => _jobId.ShouldEqual(_fakeId);

If you’re wondering about the syntax (It should_…), so take a look here: MSpec

To quote ‘Barney’ from ‘How i met your mother’: “It’s awesome!”

The code above shows the solution for case 2: Though the second assert ensures that the return value is the default value, the first assert is the important one as it guarantees me that there was no read operation on the database

If i want to indicate case 1, i just have to rewrite line 5 into “.MustHaveHappened()”.

Fake It Easy – WithArgumentsForConstructor

Dieser Tage stand ich vor dem Problem, dass ich bei einem Test der zu testenden Klasse prüfen musst, ob ein Aufruf innerhalb der Klasse stattgefunden hat.

Beispiel: Die Klasse Vb6Process enthält die 2 Methoden Monitor() und Kill(). Die Monitormethode ruft unter bestimmten Umständen, welche ich in meinem Arrange bzw. Act des Unit Tests provoziert habe, die Kill() Methode auf. Und genau das wollte ich prüfen. Nun ist es natürlich sinnfrei das zu mocken, was man testen will.

Fake It Easy bietet uns dafür eine Lösung, nämlich die Methode WithArgumentsForConstructor. Bei meinem Code sieht das wie folgt aus:

   1: _vb6Process = A.Fake<Vb6Process>(

   2:     x => x.WithArgumentsForConstructor(

   3:         () => new Vb6Process(

   4:               jobController,

   5:               token,

   6:               printServiceConfig)

   7:           )

   8:       );

 

Folgendes ist zu beachten:

  • In Zeile 1 wird nicht wie üblich ein Interface, sondern die konkrete Klasse angegeben
  • Über die Methode WithArgumentsForConstructor wird dann die konkrete zu testende Instanz reingereicht. Wichtig ist hierbei, dass eine Expression verwendet wird. Ein Delegate ist nicht möglich!

Damit kriegt Fake It Easy alle Aufrufe innerhalb einer Klasse mit, sodass ich im Assert über

   1: () => A.CallTo(() => _vb6Process.Kill()).MustHaveHappened()

 

prüfen kann, ob die Kill-Methode aufgerufen wurde.

Wichtig: Folgender Aufruf hätte mich nicht zum Ziel geführt

   1: _vb6Process = A.Fake<IVb6Process>(

   2:     x => x.Wrapping(

   3:         new Vb6Process(

   4:             jobController,

   5:             token,

   6:             printServiceConfig

   7:         )

   8:     )

   9: );

 

In diesem Fall hätte ich nur einen Wrapper um die Klasse herum, d.h. ich würde nur Aufrufe von außerhalb auf die Instanz mitbekommen, nicht aber Aufrufe innerhalb der Klasse! Ein riesiges Dankeschön an dieser Stelle an Alex Groß für seine Hilfe dabei.

LINQ Gruppierung und Sortierung mit VB.NET

Wer einmal vor der Aufgabe stehen sollte, dass er in einer Tabelle nach einem Kriterium gruppieren und nach der Anzahl der Vorkommen in der Gruppe sortieren muss, dem wird folgender Code helfen:

   1: Using dataContext = _dataContextInitializer.GetDataContext(True)

   2:     Dim states = From pj In dataContext.PrintJobs

   3:         Where (pj.CreateDate > _backwardTimeSpan)

   4:         Group pj By pj.StatusId Into Group

   5:         Order By Group.Count Descending

   6:         Select New With {.statusId = StatusId, Group.Count}

   7: End Using

Was wird genau gemacht? Ich hole mir alle Druckaufträge, deren Erstelldatum größer einem von mir definierten Datum sind. Danach gruppiere ich anhand der StatusId (z.B. fertig, zum Drucker gesendet, etc.) und zu guter Letzt sortiere ich die StatusIds anhand der Häufigkeit ihres Vorkommens.

Liste in DataTable konvertieren

Wer aus welchem Grund auch immer einmal vor dem Problem steht eine Liste in ein DataTable konvertieren zu müssen, kann folgendes Code Snippet verwenden:

1: Public Function ConvertListToDataTable(objects As System.Collections.IList)

As DataTable Implements cwContracts.IConverter.ConvertListToDataTable

   2:     Dim dataTable As New DataTable

   3:  

   4:     If objects IsNot Nothing AndAlso objects.Count > 0 Then

   5:         'Erzeuge Spalten aus den Properties des Objekts

   6:         Dim objectProperties = objects(0).GetType.GetProperties()

   7:         For Each prop In objectProperties

   8:             Try

   9:                 dataTable.Columns.Add(prop.Name, prop.PropertyType)

  10:             Catch ex As NotSupportedException

  11:                 dataTable.Columns.Add(prop.Name)

  12:             End Try

  13:         Next

  14:  

  15:  

16: 'Erzeuge die DataRows und fülle die passenden Objektwerte in die

passenden Spalten

  17:         Dim newRow = dataTable.NewRow

  18:  

  19:         For Each item In objects

  20:             newRow = dataTable.NewRow()

  21:  

  22:             For Each prop In item.GetType.GetProperties()

  23:                 newRow(prop.Name) = prop.GetValue(item, Nothing)

  24:             Next

  25:  

  26:             dataTable.Rows.Add(newRow)

  27:         Next

  28:     End If

  29:  

  30:     Return dataTable

  31: End Function

Mit einem Converter könnt ihr das auch in C# Code konvertieren.

Beachtet, dass ihr das try-catch in Zeile 8-12 nur verwenden müsst, wenn in der Liste Nullable Values sein können. Das wäre beispielsweise der Fall, wenn ihr mit dem Entity Framework bzw. LINQ auf eine Datenbanktabelle zugegriffen habt, die Felder beinhaltet, die null sein dürfen. Wenn es euch nicht wichtig ist, dass die Columns im DataSet typisiert sind, könnt ihr auch einfach nur Zeile 11 verwenden.

%d Bloggern gefällt das: