Schlagwort-Archive: Mocking

Fakes, Mocks, statischer Code und fehlende Interfaces

Kürzlich habe ich meinen Kollegen aus der PHP Entwicklung gezeigt, wie man mit statischem Code aus dem Framework umgeht, zu dem es keine Interfaces gibt. Heute kam auf der User Group Karlsruhe nochmals das Thema in einem Dialog auf sodass ich das Beispiel hier publizieren will.

Dazu dient mir die Console-Klasse aus dem .NET Framework. Ich könnte jetzt auf die häufig verwendete WriteLine-Methode eingehen, allerdings will ich die Aufgabe ein wenig erschweren, indem ich die ReadKey-Methode mocken bzw. faken will. Damit haben wir auch gleich das Thema User Interaktion abgedeckt.

Zunächst noch eine Klarstellung: Von Fakes spricht man, wenn man Dummy-Implementierungen selbst schreibt. Just in diesem Moment habe ich Dr. House geschaut und da kam der Ausspruch “das war bloß gefakt” vor. Eine gute Eselbrücke, wie ich finde, da man für einen Fake eben aktiv werden muss. Mocking bezeichnet das automatische Erstellen von Fakes anhand von Interfaces dynamisch zur Laufzeit. Die dazu benötigten Proxies werden können mit Mocking Frameworks erzeugt werden. Beide Verfahren werde ich zeigen.

Schauen wir uns den eigentlichen Code an, den wir dann verbessern, um ihn zu testen:

   1: public void Process()

   2: {

   3:     var userChoice = Console.ReadKey();

   4:     switch (userChoice.Key)

   5:     {

   6:         case ConsoleKey.S:

   7:             //Logik A ausführen

   8:             break;

   9:  

  10:         default:

  11:             //Logik B ausführen

  12:             break;

  13:     }

  14: }

Wie können wir das testen? Ideal wäre, wenn es ein Interface für die Console Klasse gäbe. Gibt es aber nicht. Also erstellen wir unser eigenes:

   1: public interface IConsoleHelper

   2: {

   3:     ConsoleKeyInfo ReadKey();

   4: }

Die Implementierung für unser eigentliches Programm ist das nichts anderes als ein Wrapper für die ursprüngliche Logik:

   1: public class ConsoleHelper : IConsoleHelper

   2: {

   3:     public ConsoleKeyInfo ReadKey()

   4:     {

   5:         return Console.ReadKey();

   6:     }

   7: }

So weit so gut. Nun muss das ursprüngliche Programm diese Abhängigkeit reingereicht bekommen. Wie, das ist euch überlassen. Entweder ihr übergebt die Implementierung per  Constructor Injection oder per Method Injection. Ich halte es immer so, dass alle für den eigentlich Logikablauf essentielle Abhängigkeiten über den Konstruktor reingereicht und alle optionalen Abhängigkeiten (wie einen Logger) über eine Methode (z.B. SetLogger(ILogger logger)) injiziert werden. Egal wie ihr das implementiert, innerhalb des Programms gibt es dann die Instanzvariable _consoleHelper vom Typ IConsoleHelper. Der Code sieht dann wie folgt aus:

   1: public void Process()

   2: {

   3:     var userChoice = _consoleHelper.ReadKey(); //<- Hier liegt der Unterschied

   4:     switch (userChoice.Key)

   5:     {

   6:         case ConsoleKey.S:

   7:             //Logik A ausführen

   8:             break;

   9:  

  10:         default:

  11:             //Logik B ausführen

  12:             break;

  13:     }

  14: }

 

Nun zum Testen. Euer Unit Test müsste nun prüfen, o bei der Eingabe von S Logik A und bei sonstigen Eingaben Logik B ausgeführt wird.

Zuerst die Möglichkeit mit einem selbstgeschriebenen Fake, den ich zum Testen des ersten Falls, nämlich der Eingabe von S, nehme:

   1: /// <summary>

   2: /// Eigene Fake-Implementierung zum faken von Konsoleneingaben

   3: /// </summary>

   4: public class ConsoleFake : IConsoleHelper

   5: {

   6:     public ConsoleKeyInfo ReadKey()

   7:     {

   8:         return new ConsoleKeyInfo('s', ConsoleKey.S,false,false,false);

   9:     }

  10: }

 

Und hier die Implementierung eines Mocks mit dem Framework Fake It Easy:

   1: var consoleFake = A.Fake<IConsoleHelper>();

   2: A.CallTo(() => consoleFake.ReadKey())

   3:        .Returns(new ConsoleKeyInfo(

   4:             'a', ConsoleKey.A, false, false, false)

   5:         );

 

Schaut euch auch den Artikel über das Mocken von DateTime.Now an.

Werbung

Data Annotations für valide DTOs

Wer viel mit DTOs arbeitet, der sollte sich in jedem Fall Data Annotations aus dem .NET Framework anschauen. Ich habe diese kürzlich wieder für ein DTO zur Speicherung der Database Configuration verwendet. Das DTO sieht wie folgt aus:

   1: public abstract class DatabaseConfig

   2: {

3: [System.ComponentModel.DataAnnotations.Required(ErrorMessage = "Bitte den

Instanznamen der Datenbank angeben.")]

   4:     public string InstanceName { get; set; }

   5:  

6: [System.ComponentModel.DataAnnotations.Required(ErrorMessage = "Bitte den

Datenbanknamen angeben.")]

   7:     public string DatabaseName { get; set; }

   8:  

   9:     public string UserName { get; set; }

  10:  

  11:     public string Password { get; set; }

  12:  

13: [System.ComponentModel.DataAnnotations.Required(ErrorMessage = "Bitte die

Einstellung für IntegratedSecurity setzen.")]

  14:     public bool IntegratedSecurity { get; set; }

  15: }

 

Dabei handelt es sich nur um ein sehr einfach Beispiel. Es ist problemlos auch möglich Ranges für Zahlen anzugeben, String-Längen zu definieren oder gar ganz eigene Annotations zu bauen, um z.B. Emailadressen zu validieren.

Was jetzt noch fehlt ist eine Implementierung eines Validator. Ich habe das Interface wie folgt definiert:

   1: public interface IValidator<in TIn>

   2: {

   3:     /// <summary>

   4:     /// Liefert die Fehlermeldungen zu allen ungültigen Werden als String zurück

   5:     /// </summary>

   6:     /// <param name="input"></param>

   7:     /// <returns></returns>

   8:     string ToString(TIn input);

   9:  

  10:     /// <summary>

  11:     /// Liefert eine Auflistung aller ungültigen Werte

  12:     /// </summary>

  13:     /// <param name="input"></param>

  14:     /// <returns></returns>

  15:     ICollection<string> GetMessages(TIn input);

  16:  

  17:     /// <summary>

  18:     /// Prüft, ob das zu validierende Objekt gültig ist

  19:     /// </summary>

  20:     /// <param name="input">Das zu validierende Objekt</param>

  21:     /// <returns>true, wenn valide</returns>

  22:     bool IsValid(TIn input);

  23: }

 

Die Implementierung speziell für die Validierung von Data Annotations (das Interface lässt sich schließlich auch noch für andere Validierungen verwenden) sieht so aus:

   1: public class DataAnnotationsValidator<TIn> : IValidator<TIn>

   2: {

   3:     public string ToString(TIn input)

   4:     {

   5:         var invalidProperties = new StringBuilder();

   6:         foreach (var errorMessage in GetErrors(input))

   7:         {

   8:             invalidProperties.AppendLine(errorMessage);

   9:         }

  10:  

  11:         return invalidProperties.ToString();

  12:     }

  13:  

  14:     public ICollection<string> GetMessages(TIn input)

  15:     {

  16:         return GetErrors(input).ToList();

  17:     }

  18:  

  19:     public bool IsValid(TIn instance)

  20:     {

  21:         var invalidProperties = from prop in typeof (TIn).GetProperties()

22: from attribute in prop.GetCustomAttributes(false).

OfType<ValidationAttribute>()

  23:                                 where !attribute.IsValid(prop.GetValue(instance, null))

  24:                                 select attribute;

  25:  

  26:         return invalidProperties.Count() == 0;

  27:     }

  28:  

  29:     private static IEnumerable<string> GetErrors(TIn instance)

  30:     {

  31:         return from prop in typeof(TIn).GetProperties()

  32:                from attribute in prop.GetCustomAttributes(false).OfType<ValidationAttribute>()

  33:                where !attribute.IsValid(prop.GetValue(instance, null))

  34:                select attribute.FormatErrorMessage(prop.Name);

  35:     }

  36: }

 

Und schon in der Bibel steht geschrieben: Entwickle testgetrieben. Deswegen noch zum Abschluss meine Specs (alles in eine Klasse mit dem Namen DataAnnotationsValidatorSpecs.cs einfügen), geschrieben mit dem Framework Machine.Specifications (gibt es auch als NuGet-Package: install-package Machine.Specifications):

   1: public class DataAnnotationsValidatorTestBase

   2: {

   3:     protected static DtoTestClass Config = new DtoTestClass();

   4:  

   5:     public class DtoTestClass

   6:     {

7: [System.ComponentModel.DataAnnotations.Required(ErrorMessage = "Bitte den

Datenbanknamen angeben.")]

   8:         public string Database { get; set; }

   9:         public string Server { get; set; }

  10:         public string User { get; set; }

  11:     }

  12: }

  13:  

  14: public class When_an_invalid_object_is_passed : DataAnnotationsValidatorTestBase

  15: {

  16:     Establish context = () =>

  17:     {

  18:         _validator = new DataAnnotationsValidator<DtoTestClass>();

  19:         Config.Database = string.Empty;

  20:  

  21:     };

  22:  

  23:     private Because of = () => _isValid = _validator.IsValid(Config);

  24:  

  25:     private It should_evaluate_it_as_invalid = () => _isValid.ShouldBeFalse();

  26:  

  27:     private static DataAnnotationsValidator<DtoTestClass> _validator;

  28:     private static bool _isValid = true;

  29: }

  30:  

  31: public class When_an_valid_object_is_passed : DataAnnotationsValidatorTestBase

  32: {

  33:     Establish context = () =>

  34:     {

  35:         _validator = new DataAnnotationsValidator<DtoTestClass>();

  36:         Config.Database = "bac";

  37:  

  38:     };

  39:  

  40:     private Because of = () => _isValid = _validator.IsValid(Config);

  41:  

  42:     private It should_evaluate_it_as_valid = () => _isValid.ShouldBeTrue();

  43:  

  44:     private static DataAnnotationsValidator<DtoTestClass> _validator;

  45:     private static bool _isValid = false;

  46: }

  47:  

  48: public class When_an_object_has_no_annotations

  49: {

  50:     internal class test

  51:     {

  52:  

  53:     }

  54:  

  55:     Establish context = () =>

  56:     {

  57:         _validator = new DataAnnotationsValidator<test>();

  58:  

  59:     };

  60:  

  61:     private Because of = () => _isValid = _validator.IsValid(new test());

  62:  

  63:     private It should_evaluate_it_as_valid = () => _isValid.ShouldBeTrue();

  64:  

  65:     private static DataAnnotationsValidator<test> _validator;

  66:     private static bool _isValid = false;

  67: }

  68:  

69: public class When_an_object_with_one_invalid_property_is_passed :

DataAnnotationsValidatorTestBase

  70: {

  71:     Establish context = () =>

  72:     {

  73:         _validator = new DataAnnotationsValidator<DtoTestClass>();

  74:         Config.Database = string.Empty;

  75:  

  76:     };

  77:  

  78:     private Because of = () => _messages = _validator.GetMessages(Config);

  79:  

  80:     private It should_evaluate_exactly_this_property_as_invalid = () =>

  81:     {

  82:         (_messages.Count == 1).ShouldBeTrue();

  83:         _messages.Aggregate(string.Empty, (current, message) => current + message)

  84:             .Contains("Datenbanknamen").ShouldBeTrue();

  85:     };

  86:  

  87:     private static DataAnnotationsValidator<DtoTestClass> _validator;

  88:     private static ICollection<string> _messages;

  89: }

LINQ und Fakes

Kürzlich stand ich vor dem Problem, dass ich einen Unit Test schreiben musste, bei dem geprüft wird, ob eine spezielle Methode aufgerufen wurde.

Beispiel: Ich habe einen Controller, der Daten von der DB in die UI lädt. Der Controller ist meine zu testende Unit. Nun will ich prüfen, ob der Controller, wenn er den Load-Befehl erhält, diesen auch korrekt an mein Repository weiterleitet. Die DB Logik habe ich dazu mit FakeItEasy gefakt. Über MustHaveHappened() kann man prüfen, ob auf dem Fake die Save-Methode aufgerufen wurde (vgl. diesen Artikel zu dem Thema).

Nun gibt es ein Problem:

1: var controlsToTranslate = from ctrl in _form.TranslatableControls select

ctrl.TranslationKey;

2: var translations = _translationRepository.LoadTranslations(_languageId,

controlsToTranslate);

Ich ermittle den zu übergebenden Parameter in meiner zu testenden Klasse per LINQ, sodass der Aufruf von Load auf meinem Repository als Parameter eine LINQ Expression ist. Damit ist der Parameter ein Objekt, welches ich in meinem Unit Test nicht nachstellen kann, sprich die Prüfung von FakeItEasy mit (vgl. Zeile 2)

   1: Fake.A

   2:     .CallTo(() => translationRepository.LoadTranslations(0, translationKeys))

   3:     .MustHaveHappened();

 

wird nie erfolgreich sein, da FakeItEasy einen Equals-Vergleich ausführt. Der zweite Parameter in meinem    Test wird immer ein anderes Objekt sein, als das, was in der zu testenden Unit (dem Controller) erzeugt wird.

Die Lösung sieht wie folgt aus:

   1: Fake.A

   2:     .CallTo(() => translationRepository.LoadTranslations(0, 

   3:      A<IEnumerable<string>>.That.Matches(x => x.SequenceEqual(translationKeys))))

   4:     .MustHaveHappened();

 

In Zeile 3 steht die Lösung für meinen Parameter. Hier sage ich, dass der Parameter vom Typ IEnumerable<string> sein muss und dass lediglich die Reihenfolge der Strings, wie sie in IEnumerable stehen, gleich sein muss. Damit habe ich den Equals-Vergleich für Parameter 2 anders implementiert.

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.

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.

%d Bloggern gefällt das: