Waarom F# prima met C# combineert

Op mijn huidige project moet ontzettend veel met getallen gegoocheld worden. De klant heeft bakken vol met berekeningen en formules om daar fantastische dingen mee te doen. Ik vind dit zeer intrigerend. Zo’n bedrijf bouwt al jaren zijn producten en heeft hierbij een hele afdeling voor softwareontwikkeling opgetuigd. Binnen het bedrijf komen en gaan de ontwikkeltools, de operating systemen, de nieuwste ideeën over software-architectuur maar er is één constante. Juist, die berekeningen?

Komt dit bekend voor?

Ik durf te stellen dat voor eigenlijk alle bedrijven hun formules (ik denk aan financiële instellingen zoals banken en verzekeraars en aan productiebedrijven met krachten, uitvloeiing van kunststof of draaicirkels) gerust als de kroonjuwelen kunne beschouwen. Iedere investering hierin om ze nog beter, sneller en betrouwbaarder te maken is een juiste keuze.

Dit betekent dus dat er een goed versiebeheer moet zijn voor de formules, een goede distributie van de gereedschapskisten waar ze inzitten (dll’s of services) en ik stel me voor dat er ook een complete geautomatiseerde teststraat (lees buildserver met minimaal unittests en een code coverage van 100 procent) voor draait. Waarom 100 procent terwijl dat een hele opgave is? De kosten tegen verkeerde resultaten uit formules wegen niet op tegen de kosten van de enorme stap van 80 procent naar 100 procent. Er kunnen bedrijven failliet gaan of zelfs mensenlevens op het spel staan.

Dus…

Formules schrijven lijkt simpel maar het kan al snel ingewikkeld worden J

Nu heb ik zelf regelmatig formules mogen implementeren en dan ga je aan de gang met static methodes om stateless te gaan rekenen en je wordt overweldigd met reeksen en complexe structuren om door te werken. En dat wordt dan dus het ijdele handwerk in C# code.

Maar de laatste weken heb ik mijn tijd doorgebracht met de PluralSight presentaties over F#. En ik vind het kijken van inhoudelijke filmpjes over een nieuw stuk technologie zowiezo heel nuttig (Het kost veel tijd om zoiets op te zetten en daarom rolt er meestal uiteindelijk echt de essentie van een onderwerp uit) maar ik ben nu heel enthousiast over F#.

F# is gewoon een extra ontwikkeltaal voor .Net en wat het speciaal maakt, is dat het een functionele taal is. Dit soort talen is juist geschikt voor zaken als recursiviteit, reeksen, enz. Alles dus waar je óf van houdt óf wat je haat 🙂

F# is vooral interessant omdat eigenlijk alles types immutable zijn. En hierdoor kan er tijdens parallelle uitvoering geen conflict optreden waarbij de ene thread een gezamenlijke waarde overschrijft.

Helaas heeft Microsoft voorheen F# vooral willen postioneren als een programmeertaal waarin je ook Winforms apps kunt maken en waarmee je ook een WPF app in elkaar kunt draaien. Persoonlijk had ik hier geen trek in, dat kon ik al met C# en ik zag niet het nut in om een nieuwe taal te leren om hetzelfde te kunnen doen wat ik al kon.

Maar F# is superieur als aanvulling op C# als het om berekeningen gaat. Kijk maar eens naar de volgende vier F# functies:


module Functions

let Func1 (x) = 2*x*x + 5*x + 3
let Func2 (x) = x*x + x
let Func3 (x) =  x*x*x - x*x + x
let Func4 (x) =  x*x*x*x + x*x*x - x*x + x

Dit is de hele inhoud van een F# file in een F# library project. En ja, dit zijn gewoon vier functies. Sterker nog, dit zijn vier methodes. Om dit te bewijzen, heb ik deze aangeroepen vanuit C#.

Het enige wat ik moest doen was een reference leggen vanuit mijn C# console app naar de F# library:

class Program
{
  static void Main(string[] args)
  {
    Console.WriteLine("CSharp Host");

    for (int i = -5; i <= 5; i++)
    {
      Console.WriteLine("Result of Func1 {0} = {1}", i, Functions.Func1(i));
    }

    for (int i = -5; i <= 5; i++)
    {
      Console.WriteLine("Result of Func2 {0} = {1}", i, Functions.Func2(i));
    }

    for (int i = -5; i <= 5; i++)
    {
      Console.WriteLine("Result of Func3 {0} = {1}", i, Functions.Func3(i));
    }

    for (int i = -5; i <= 5; i++)
    {
      Console.WriteLine("Result of Func4 {0} = {1}", i, Functions.Func4(i));
    }

    Console.WriteLine("Press a key...");

    Console.ReadKey();
  }
}

De uitkomst is dan:
F#01
Dit kan natuurlijk ook met een unit test gecontroleerd worden. F# is tenslotte een volwaardige .Net taal dus MSUnit is al voldoende:

[TestClass]
public class UnitTest1
{
  [TestMethod]
  public void TestMethod1()
  {
    // ARRANGE
    var x = 5;
    var expected = 78;

    // ACT
    var actual = Functions.Func1(x);

    // ASSERT
    Assert.AreEqual<int>(expected, actual);
}

[TestMethod]
public void TestMethod2()
{
  // ARRANGE
  var x = 5;
  var expected = 30;

  // ACT
  var actual = Functions.Func2(x);

  // ASSERT
  Assert.AreEqual<int>(expected, actual);
}

[TestMethod]
public void TestMethod3()
{
  // ARRANGE
  var x = 5;
  var expected = 105;

  // ACT
  var actual = Functions.Func3(x);

  // ASSERT
  Assert.AreEqual<int>(expected, actual);
}

[TestMethod]
public void TestMethod4()
{
  // ARRANGE
  var x = 5;
  var expected = 730;

  // ACT
  var actual = Functions.Func4(x);

  // ASSERT
  Assert.AreEqual<int>(expected, actual);
}
}

Dit is wederom simpel en werkt direct:
F#02

En de code coverage is ook 100% 🙂

F#03

Zoals je ziet wordt alles als types ook goed ingevuld. F# kent dus geen variants maar alles wordt inferred, afgeleid. Dit maakt de functies compact en flexibel.

De F# functie blijkt met integers te willen omgaan:

F#04

 

En C# snapt dit:

 

F#05

 

En:

F#06

Dus doe eens gek en ga eens naar http://www.tryfsharp.org/ en speciaal naar http://www.tryfsharp.org/Learn/getting-started . Hier kun je online in de browser kennis maken met F#.

F#07

En kijk anders eens de hilarische video op Channel 9 van Luca Bolognese

Prima entertainment en je steekt er ook nog wat van op. Al is het maar om je behoefte te vervullen om jaarlijks een programmeertaal te leren.

Meer inhoudelijke informatie over F# is te vinden op: http://fsharp.org/

 

 

 

Unittests voor MVC4 model validaties

Seeing anything you like? Google translate does not work out? Drop me a note and I will translate this post.

Dit is een vervolg op mijn eerdere blog: Custom client side validaties in ASP.NET MVC4

Als men vroeger (..) een MVC2 voorbeeld applicatie liet genereren dan kreeg men daar ook enkele custom validaties bij. Zo was er een validaties op het invoeren van tweemaal eenzelfde wachtwoord bij registratie. Dit voorbeeld werkte op class niveau om beide properties te vergelijken.

Een subtiele verbetering met de komst van MVC3 was dat dit opeens op property niveau ingesteld kon worden. Hierdoor was het mogelijk om de tekst van de validatie naast het tweede wachwoord veld te tonen.

Helaas wordt deze custom validatie niet meer gegenereerd maar is deze in het ASP.NET MVC framework als de CompareAttribute opgenomen. Gelukkig is ASP.NET MVC tegenwoordig open source en kan de broncode gedownload worden van http://aspnetwebstack.codeplex.com/SourceControl/changeset/view/3007f73f8cb2 (adres kan veranderen).

Door hier eens in te duiken heb ik een vergelijkbare validatie gebouwd. Deze controleert ook twee velden en wel of deze in de juiste volgorde gevuld zijn. Het tweede veld mag niet gevuld worden als de eerste nog niet gevuld is.

valunit1

De client side code is als volgt:


[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class CompareSequenceAttribute : ValidationAttribute, IClientValidatable
{
  private const string DefaultErrorMessage = "Fields {0} and {1} must be filled in the right sequence.";

  public CompareSequenceAttribute(string otherProperty)
    : base(DefaultErrorMessage)
  {
    if (otherProperty == null)
    {
      throw new ArgumentNullException("otherProperty");
    }

    OtherProperty = otherProperty;
  }

  public string OtherProperty { get; private set; }

  public string OtherPropertyDisplayName { get; internal set; }

  public override string FormatErrorMessage(string name)
  {
    return String.Format(CultureInfo.CurrentCulture, ErrorMessageString,
              name, OtherPropertyDisplayName ?? OtherProperty);
  }

  protected override ValidationResult IsValid(object value,
                         ValidationContext validationContext)
  {
    PropertyInfo otherPropertyInfo =
              validationContext.ObjectType.GetProperty(OtherProperty);

    if (otherPropertyInfo == null)
    {
      return new ValidationResult(String.Format(CultureInfo.CurrentCulture,
                     "Unknown {0} to compare with", OtherProperty));
    }

    object otherPropertyValue = otherPropertyInfo.GetValue(
                            validationContext.ObjectInstance, null);

    if (!string.IsNullOrWhiteSpace(value as string)
         && (string.IsNullOrWhiteSpace(otherPropertyValue as string)))
    {
      if (OtherPropertyDisplayName == null)
      {
        OtherPropertyDisplayName = ModelMetadataProviders.Current.
             GetMetadataForProperty(() => validationContext.ObjectInstance,
                  validationContext.ObjectType, OtherProperty).GetDisplayName();
      }

      return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
    }

    return null;
  }

  public IEnumerable<ModelClientValidationRule> GetClientValidationRules(
                    ModelMetadata metadata, ControllerContext context)
  {
    if (metadata.ContainerType != null)
    {
      if (OtherPropertyDisplayName == null)
      {
        OtherPropertyDisplayName = ModelMetadataProviders.Current.
                 GetMetadataForProperty(() => metadata.Model,
                         metadata.ContainerType, OtherProperty).GetDisplayName();
      }
    }

    var rule = new ModelClientValidationRule()
    {
      ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
      ValidationType = "comparesequence",
    };

    rule.ValidationParameters.Add("other", OtherProperty);

    yield return rule;
  }
}

De bijbehorende JavaScript zier er dan zo uit:

// validation name must be equal to the ValidationType in the ModelClientValidationRule

$(function () {
  jQuery.validator.addMethod('compareSequence', function (value, element, param) {
    var other = param.other;
    var otherInput = $(element.form).find(":input[id='" + other + "']")[0];

    if (value != null
      && value.length > 0
      && (otherInput.value == null || otherInput.value == '')) {
        return false;
    }

    return true;
  });

  jQuery.validator.unobtrusive.adapters.add(
               'comparesequence', ['other'], function (options) {
    var params = {
      other: options.params.other
    };

    options.rules['compareSequence'] = params;

    if (options.message) {
      options.messages['compareSequence'] = options.message;
    }
  });
} (jQuery));

Maar kunnen we deze code ook unittesten? Ja, en best eenvoudig!

De client side script unittesten is hier dan niet mee ondersteund maar de server side validatie test zal ik tonen. Overigens, heeft iemand ervaring met JavaScript unittest frameworks?

Nu is het zo dat een Validatie class een publieke IsValid methode heeft.  Helaas is het simpel aanroepen van die IsValid() methode niet voldoende, Deze validatie heeft namelijk een ValidationContext nodig om bij de waarde van de andere property te komen en die context is lastig te injecteren.

En een viewmodel met een attribuut er op mocken is ook lastig. Dus ik heb maar voor stubs gekozen.De stub ziet er dan zo uit:



public class ViewModelToValidateCompareSequenceStub
{
  public string FirstPropertyName { get; set; }

  [CompareSequence("FirstPropertyName")]
  public string SecondPropertyName { get; set; }
}

En hiermee kan ik prima unittesten. Hier is een test waarbij de validatie goed gaat:

[TestMethod]
public void CompareSequenceFirstFilledSecondFilledReturnsTrue()
{
  //// ARRANGE

  var model = new ViewModelToValidateCompareSequenceStub {
        FirstPropertyName = "Bla", SecondPropertyName = "Bla" };

  //// ACT

  var actual = Validator.TryValidateObject(model,
            new ValidationContext(model, null, null), null, true);

  //// ASSERT

  Assert.IsTrue(actual);
}

En hier is een test waarbij het valideren fout gaat en dus een false teruggeeft:

[TestMethod]
public void CompareSequenceFirstNullSecondFilledReturnsFalse()
{
  //// ARRANGE

  var model = new ViewModelToValidateCompareSequenceStub {
                                   SecondPropertyName = "Bla" };
  var results = new List<ValidationResult>();

  //// ACT

  var actual = Validator.TryValidateObject(model,
         new ValidationContext(model, null, null), results, true);

  //// ASSERT

  Assert.IsFalse(actual);
  Assert.IsNotNull(results);
  Assert.AreEqual(1, results.Count);
  Assert.AreEqual(
    "Fields SecondPropertyName and FirstPropertyName must be filled in the right sequence.",
                             results[0].ErrorMessage);
}

Zoals te zien is, test ik ook op de specifieke foutmeldingen. Ik kan bv. ook een melding krijgen dat de opgegeven property niet bestaat. En er kunnen zelfs meerdere validaties gefaald hebben.

Conclusie

Het unittesten van validaties op viewmodellen is eenvoudig als men gebruik maakt van stubs. Enige nadeel is dat er geen controle is over de waarden die op de attributen ingesteld worden. Er moet dus voor andere varianten aparte stubs ingevuld worden.

Arrange parameters in Unittest naam

Over Unittesten valt heel wat te vertellen. Je kunt natuurlijk vanuit Test Driven Development naar unttests kijken en over red/green/refactor beginnen. Maar ik ben geen ster in TDD.

Als ik heel eerlijk ben, dan heb ik een haat/liefde verhouding met unittesten. In vind het heel lastig om in de praktijk direct met testen te beginnen. Ik moet eerst nog de werking van de code en de daarbij behorende methodes en interfaces gaan ontdekken. Ik werk dus liever vanuit de gedachte Green/Red/Refactor. Dus ik schrijf liever werkende code en daarna de unittests aansluitend op die code en refactor eventueel mijn code later.

En inderdaad, achteraf baal ik er van dat ik niet meer tests heb geschreven… Want unittests leveren zo ontzettend veel op: zeer snel de werking van je code controleren, dag in, dag uit, zelfs na vele maanden.

Niks geeft meer voldoening dan code die al maanden niet meer aangeraakt is, na het ophalen uit TFS, direct te kunnen compileren en daarna de groene unittests te zien ‘binnenstromen’.

Maar vervolgens heb ik een nieuw probleem. Naar mate er meer unittests verschijnen, moet ik ze ook uit elkaar weten te houden. Ik gebruik hierbij in eerste instantie de naamgeving: [UnitOfWork_StateUnderTest_ExpectedBehavior]. Dit is ook beschreven door Roy Osherove.

Ik combineer dit met de strategie ARRANGE, ACT, ASSERT.

Hier is een voorbeeld:

[TestMethod]
public void DoSomething_SimpleInput_Works()
{
  // Arrange

  string parameter = "Bla";
  string expected = "BlaBla";

  // Act

  string actual = SomeClass.DoSomething(parameter);

  // Assert

  Assert.AreEqual(expected, actual);
}

Maar stel dat ik meerdere unittests heb die gelijkwaardige scenario’s wil testen dan is deze naamgeving nogal eentonig… Daarom maak ik liever gebruik van een wat uitgebreidere naamgeving:

[UnitOfWork_StateUnderTest_ExpectedBehavior_Parameters].
Hieronder volgt een simpel voorbeeld hiervan:


[TestMethod]
public void DoSomething_SimpleInput_Works_Bla_BlaBla()
{
  // Arrange

  string parameter = "Bla";
  string expected = "BlaBla";

  // Act

  string actual = SomeClass.DoSomething(parameter);

  // Assert

  Assert.AreEqual(expected, actual);
}

Met dit formaat kun je verfijnder de unittesten uit elkaar houden. Kijk maar eens naar onderstaande window. Handig dus.

Maar er is wel een keerzijde. Je moet de parameters herhalen. Dezelfde waarden komen zowel in je code als in je methodenaam voor.
Dit is te ondervangen met het gebruik van de StackFrame class. Deze is eigenlijk bedoeld om runtime toegang tot je stack te hebben , maar ik kan de methodenaam nu runtime uitlezen en toepassen:


[TestMethod]
public void DoSomething_SimpleInput_Works_Bla_BlaBla()
{
  // Arrange

  StackFrame stackFrame = new StackFrame();
  string methodName = stackFrame.GetMethod().Name;

  string parameter = methodName.Split('_')[3];
  string expected = methodName.Split('_')[4];

  // Act

  string actual = SomeClass.DoSomething(parameter);

  // Assert

  Assert.AreEqual(expected, actual);
}

Als je dus het heilige vuur krijgt en snel een hele groep unittests wilt creëren dan is het heel eenvoudig geworden om de unittests te kopiëren en slecht de naam aan te passen. Deze moet uiteindelijk toch uniek en beschrijvend zijn.
Natuurlijk is dit vooral zinvol voor eenvoudige unittests met simpele types. Echt unittesten in Bulk is interessanter met de het DataSource attribuut.
Maar dit is een aardige uitbreiding van mijn unittest skills.