TestAutomatisering & PerformanceTesten

Creëer meer eenheid in je SpecFlow steps met Step Argument Transformations

Laatst kreeg ik de vraag, “Hoe maak je in je testdata onderscheid tussen een regular expression en een gewone tekst”. Oftewel: Hoe beheer je verschillende soorten steps als ze alleen verschillen in de manier waarop data vergeleken wordt. Je hebt een veld en je wilt controleren of er een bepaalde waarde in staat, maar soms ook of het matcht aan een pattern, of het een waarde bevat of misschien dat het een bepaalde minimum lengte heeft:

# exact value 
Then the name field is equal to "Douglas Adams"

# contains a value
Then the name field contains "Adams"

# matching a pattern
Then the name field is matching the regex "[A-Z][a-z]+ [A-Z][a-z]+"

# minimum field length
Then the name field contains at least 2 characters

Je zal nu geneigd zijn om 1 van de volgende oplossingen te kiezen:

1. Voor elk check-type maak je een aparte stepdefinitie aan. Dit levert in dit geval 4 verschillende steps op die marginaal verschillen in de Assert sectie.
2. Je maakt één step die alle verschillende gevallen aankan door middel van een switch of een if/else if boom.

Beide zijn niet echt ideaal te noemen: Je zal het waarschijnlijk voor veel meer velden willen gaan inbouwen wat óf een wildgroei aan steps op gaat leveren, óf heel veel herhalende code in de vorm van switch statements.

Ik ben al een tijd fan van Fluent Assertions, een library die je assertions in menselijke taal laat noteren. Wat nu als we dat gedrag kunnen nabootsen en daar een generiek component voor gaan schrijven:

# exact value 
Then the name field should be "Douglas Adams"

# contains a value
Then the name field should contain "Adams"

# matching a pattern
Then the name field should match regex "[A-Z][a-z]+ [A-Z][a-z]+"

# minimum field length
Then the name field length should be greater or equal to "2"

De gegenereerde code ziet er als volgt uit:

[Then(@"the name field should be ""(.*)""")]
public void ThenTheNameFieldShouldBe(string expectedValue)
{
  ScenarioContext.Current.Pending();
}

We kunnen de daadwerkelijk compare actie er nu uit halen en die voeden in een switch statement:

[Then(@"the name field (.+) ""(.*)""")]
public void ThenTheNameFieldShouldBe(string compareAction, string expectedValue)
{
  // normally the actual value is retrieved from your application
  var actualValue = "Douglas Adams";

  switch (compareAction)
  {
    case "should be":
      actualValue.Should().Be(expectedValue);
      break;
    case "should contain":
      actualValue.Should().Contain(expectedValue);
      break;
    case "should match regex":
      actualValue.Should().MatchRegex(expectedValue);
      break;
    case "length should be greater or equal to":
      actualValue.Length.Should().BeGreaterOrEqualTo(Convert.ToInt32(expectedValue));
      break;
    default:
      throw new ArgumentException("Compare action is not a valid action");
  }
}

Dit levert nog geen reusable code op, maar je ziet wellicht waar dit heen gaat: we kunnen dit deel ook weer generiek maken. Dat doen we door een step argument transformatie. Hiervoor zetten we een class in de argumentenlijst, SpecFlow zal nu proberen dit te converteren naar dit object met behulp van een zogenaamde StepArgumentTransfomation:

[Then(@"the name field (.+) ""(.*)""")]
public void ThenTheNameFieldShouldBeComparable(ShouldComparer<string> compareAction, string expectedValue)
{
  // normally the actual value is retrieved from your application
  var actualValue = "Douglas Adams";
  
  // ShouldComparer action here...
}

De ShouldComparer moet gevoed worden met de actie die het moet uitvoeren en bij een call naar een Execute method de daadwerkelijke actie ook uitvoeren:

public class ShouldComparer<T1>
{
  private readonly Action<T1, T1> _action;
  
  public ShouldComparer(Action<T1, T1> action)
  {
    this._action = action;
  }

  public void Execute(T1 actual, T1 expected) => _action(actual, expected);
}

Als het T1 type je wat verward: In dit geval maken we gebruik van Generics. De T1 staat voor het type waarmee de class wordt aangemaakt, zodat we niet alleen de actie op string types uit kunnen voeren, maar op elk type dat wordt ondersteund door Fluent Assertions. In ons geval maken we een ShouldComparer aan voor strings (dat wordt gedaan door definitie ShouldComparer<string> compareAction in de stepmethod parameters), maar zo kunnen we de ShouldComparer later ook voor andere object typen gebruiken.

Nu volgt de implementatie: we moeten SpecFlow vertellen hoe het een string zoals “should be” of “should match regex” moet ombouwen naar de juiste actie. Hiervoor maken we een nieuwe StepArgumentTransformation methode aan die in een public class met een binding attribute moet staan. De methode moet een parameter ontvangen van het type waarvan we willen converteren en een object teruggeven van het type waarnaar we willen converteren.

[Binding]
public class ShouldTransformations
{
  [StepArgumentTransformation]
  public ShouldComparer<string> ShouldTransformationWithTypedComparer(string term)
  {
    switch (term)
    {
      case "should be":
        return new ShouldComparer<string>((actual, expected) 
          => actual.Should().Be(expected));
      case "should contain":
        return new ShouldComparer<string>((actual, expected) 
          => actual.Should().Contain(expected));
      case "should match regex":
        return new ShouldComparer<string>((actual, expected)
          => actual.Should().MatchRegex(expected));
      case "should be greater or equal to":
        return new ShouldComparer<string>((actual, expected) 
          => actual.Length.Should().BeGreaterOrEqualTo(Convert.ToInt32(expected)));
      default:
        throw new ArgumentException("the term was not recognized");
    }
  }
}

In dit geval staan hier alleen de vier gevallen die in het scenario staat aangegeven, maar in de praktijk zal je hier alle gevallen zetten die je (potentieel) wilt gebruiken zoals Should().NotBe() of Should().Match().

Nu hoeven we alleen nog de Execute method aan te roepen in de stepdefinitie:

[Then(@"the name field (.+) ""(.*)""")]
public void ThenTheNameFieldShouldBeComparable(ShouldComparer<string> compareAction, string expectedValue)
{
  var actualValue = "Douglas Adams";
  compareAction.Execute(actualValue, expectedValue);
}

Bij nieuwe steps hoef je in je stepdefinitie alleen nog maar een ShouldComparer toe te voegen en je hebt alle functionaliteit die je al eerder hebt geïmplementeerd voor een compare. Als je andere en/of eigen objecten wilt comparen, dan kan dat ook. Je kan je eigen StepArgumentTransformation schrijven voor elk type dat je wilt comparen, of je gebruikt de generieke implementatie:

[StepArgumentTransformation]
public ShouldComparer<T1> ShouldTransformationWithTypedComparer<T1>(string term)
{
  switch (term)
  {
    case "should be":
      return new ShouldComparer<T1>((actual, expected) => actual.Should().Be(expected));
    case "should not be":
      return new ShouldComparer<T1>((actual, expected) => actual.Should().NotBe(expected));
    
    default:
      throw new ArgumentException("the term was not recognized");
  }
}

Deze comparer kan je feeden met elke willekeurige class en de Fluent Assertions library zal automatisch een compare maken.

De CompareAction class kan je eventueel ook nog fluent maken zodat er een nog beter leesbaar statement uit voortkomt: compare.Actual(actualValue).With().Expected(expectedValue);

Conclusie:

In bepaalde SpecFlow steps wil je vaak meer testen dan alleen gelijkheid van data. Het inbouwen van een mechanisme om ook te vergelijken op bijvoorbeeld ongelijkheid, het matchen op een pattern of een minimum veldlengte kan resulteren in een wildgroei aan steps of het opblazen van de step methodes met veel herhaling. Door gebruik te maken van Fluent Assertions en Step Argument Transformations hebben we een generiek bruikbaar patroon die de vergelijkingsactie lostrekt van de overige acties in de glue code. Hiermee maken we onze code meer DRY en verkrijgen we een hogere mate van Separation of Concerns.

Dit bericht is geplaatst in Testautomatisering en getagd in Fluent Assertions SpecFlow

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *

Nieuws

Blijf op de hoogte

Visual Regression Testing – Wat is het en wat heb je eraan?

07/11/2018

Visual Regression Testing, of visuele regressie testen, is een categorie van testen die zich focust op het identificeren van visuele wijzigingen tussen iteraties of versies van een website. Dit kan handmatig door schermen of schermafdrukken te vergelijken, maar het is beter herhaalbaar en sneller te testen door dit automatisch te doen. Het mooiste is om deze testen als een […]

Meer efficiency en flexibiliteit in API’s

04/10/2018

Er wordt steeds meer gebruik gemaakt van GraphQL API. Tijdens mijn laatste opdracht heb ik hier dan ook mee gewerkt.. Graag deel ik mijn ervaringen hierover in een aantal blogs. In deze eerste blog wil ik het graag hebben over de uitdagingen met REST API en hoe GraphQL deze oplost. Maar eerst even een korte […]

Impact tooling op performance: Dynatrace

21/09/2018

In deze blog geef ik kort de resultaten weer van een onderzoekje dat ik bij één van mijn laatste opdrachten heb gedaan naar de impact van het gebruik van de tool Dynatrace op de infrastructuur waar het op draait. Mocht je gebruik maken van tooling als Dynatrace of op een andere manier geïnteresseerd zijn in […]

Tips en tricks voor Protractor

06/09/2018

Vanuit PerformanceArchitecten doen we graag aan kennisdeling. Van tool reviews tot aan complete best practices maar ook van een aantal simpele tips en tricks die je kan gebruiken in je dagelijkse werk. Vandaag in die laatste categorie voor gebruikers van Protractor: (Javascript) timing issues en het managen van de volgorde van uitvoeren van actions. Inleiding […]

PerformanceArchitecten @The Next Web conferentie

04/07/2018

Was het een interessante conferentie? Beslist. Heeft TNW PerformanceArchitecten op nieuwe ideeën gebracht? Zeker. Heb jij TNW gemist, maar wil je je wel in vijf minuten op de hoogte laten brengen? Lees dan snel verder! En mocht je er wel zijn geweest.. We zijn benieuwd naar jouw beeld! Wat is de Next Web? Voor hen […]

De ‘waar begin ik met testautomatisering’ handleiding.

11/04/2018

Wij worden regelmatig gevraagd te helpen bij het opzetten of verbeteren van testautomatisering in een Agile omgeving bij een klant. En hoeveel ervaring je ook hebt, wat je opdracht ook precies inhoudt en in welk team je ook terechtkomt, het is altijd even zoeken waar te beginnen. Onderstaand stappenplan ondersteunt hierbij. PerformanceArchitecten organiseert veel kennissessies. Zo […]

Wat is het Bug Filter?

26/03/2018

Een algemeen gedeelde opvatting over het doel van testen binnen software ontwikkeling is “het tegenhouden van bugs en het afdekken van risico’s”. Over hoe dit vervolgens het best valt te organiseren, bestaan er echter vaak veel meningsverschillen. In deze 2 minute snack sta ik graag even stil bij een inzicht dat verhelderend werkt in deze […]

Testen? Begin bij de basis! | Het belang van unittesten

19/02/2018

Inleiding Mijn vrouw en ik zijn op dit moment bezig met het bouwen van een huis. In dit geval niet als bouwvakker of aannemer, maar dan toch wel als opdrachtgever. Spannend vinden we het zeker, leuk ook. Wat heeft dit te maken met unittesten zou je denken. Nou, het volgende… Het huis wordt namelijk gebouwd […]

De Absolute Beginners Guide voor API Testen met rest-assured.io

16/01/2018

Omdat het moeilijk was om een eenvoudige tutorial te vinden voor rest-assured.io, ben ik na eerst zelf uit te vinden hoe het werkt, maar eens begonnen met een tutorial die de absolute basics uitlegt over rest-assured. In deze post laat ik zien hoe we op een zo eenvoudig mogelijke manier een API test kunnen maken met […]

APACHE MPM (op *nix servers)

07/11/2017

Benieuwd naar de impact van het wijzigen van Apache MPM Prefork naar Worker? Lees dan door! Bij één van onze klanten heb ik dit onderzocht. Omdat dit ook interessant kan zijn voor anderen, deel ik mijn resultaten en ervaringen graag. Het is misschien wat technisch allemaal, maar voor performancetesters, de doelgroep, is het vast goed […]

Performancetesten en CI/CD, gaat dat samen?

13/10/2017

De afgelopen najaarseditie van Testnet stond onder het thema Continuous Everything vooral stil bij CI/CD en natuurlijk testen. Gezien DevOps en CI/CD ook grote invloed hebben op het vakgebied performance (testen), zijn wij blij dat we vanuit PerformanceArchitecten een bijdrage mochten leveren door middel van het delen van onze visie hierop. Onze collega René Meijboom […]

Een eerste indruk van Gauge

08/09/2017

Tijdens één van onze kennismiddagen hebben we gekeken naar het testtool Gauge. Doel van de sessie was om een beeld te krijgen wat de toegevoegde waarde van Gauge is voor een tester. Benieuwd naar onze ervaringen? Lees dan snel verder! Gauge is een open source project, gesponsord door ThoughtWorks en belooft in het kort het […]