[x^2 for x in lst]

AAA - Arrange Act Assert

2014-09-19

Idag när jag satt och skrev ett enhetstest erinrade jag mig att jag sett en kollega lägga in tre rubriker i sina enhetstest. Han använde alltid samma rubriker: Arrange, Act och Assert. När jag såg honom skriva ett enhetstest och lägga in de rubrikerna kommer jag ihåg att jag tänkte något i stil med "Det där ser bra ut. Det skall jag komma ihåg att använda!". Sen glömde jag bort det.

Idag kom jag som sagt i alla fall ihåg att jag sett det och att jag hade tyckt att det var bra. Eftersom kollegan gått för dagen beslöt jag mig för att googla lite för att kolla om man kunde hitta något på nätet. Det kan man ju nästan alltid. Om det inte gäller objektdatabasen Versant i alla fall. Det bästa man kan hoppas på då är en länk till någon obskyr artikel på franska. Huga. Tur att man inte behöver jobba med den databasen längre. MongoDB är en så otroligt mycket trevligare bekantskap. Oracle också förresten och MySQL och Postgres. Egentligen är de flesta databaser ganska trevliga att jobba med. Utom Versant.

Åter till ämnet. När jag sökte på nätet efter unit test arrange act assert fick jag en hel del träffar. Det visade sig att Arrange Act Assert var ett enhetstestmönster som beskrevs 2003. Hoppsan. Det hade jag verkligen missat. Det känns som om man verkligen skulle behöva fortbilda sig de där tre timmarna om dagen som Uncle Bob förespråkar i Clean Code för att hinna hänga med som man borde. Fattar bara inte hur man skall hinna med det. I varje fall visade googlingen att Arrange-Act-Assert är ett allmänt spritt mönster.

Vad går då Arrange-Act-Assert-mönstret ut på? Jo, det går ut på att man delar in sina enhetstest i tre delar där koden i varje del är ansvarig för en viss väldefinierad uppgift. Att de olika delarna kallas för Arrange, Act och Assert är kanske inte så svårt att lista ut, men vad skall då ingå i varje del? Jo...

  • Arrange: sätta upp preconditions och skapa data som skall skickas in till koden som skall testas.
  • Act: anropa koden som skall testas.
  • Assert: verifiera att exekveringen gav det förväntade resultatet.

Ett litet exempel kan kanske var på sin plats. Antag att vi har en klass DoStuffer som har en metod int multiplyByTwo(int). Antag att vi vill enhetstesta den här metoden. Hur kan då ett enhetstest som använder Arrange-Act-Assert-mönstret se ut? Kanske så här:

  @Test
  public int testMultiplyByTwo() {
    // Arrange
    int numberToMultiply = 42;

    // Act
    int restult = unitUnderTest.multiplyByTwo(numberToMultiply);

    // Assert
    assertEquals("", numberToMultiply * 2, result);
  }
}

Vi har här delat upp testet i de tre delar som AAA-mönstret föreskriver. I den första delen, Arrange, sätter vi upp indatat som vi skall mata in till metoden vi skall testa. Här kan man även sätta upp mock-objekt och vilka metodanrop man förväntar sig på dem om det nu är så att man använder något mock-ramverk som t.ex. EasyMock eller Mockito.

I den andra delen, Act utför vi själva anropet till det vi vill testa. Det är också det enda vi gör i den här delen.

I den tredje och sista delen, Assert, verifierar vi att anropet till den metod som skulle testat gav det förväntade resultatet. Om mockobjekt används kan vi här verifiera att de förväntade metodanropen gjordes på dem.

Vad är då fördelen med Arrange - Act - Assert-mönstret? Det verkar ju inte vara något speciellt stort och grandiost mönster direkt. Som jag ser det finns det en rad olika fördelar. En fördel är att det är lätt att se var de olika delarna i testet finns när dessa är väl avgränsade. Kommer man in som ny utvecklare och skall ändra eller utöka ett befintligt test är det lätt att hitta var koden man behöver ändra finns. Behöver man ändra indatat görs detta i Arrange-delen, behöver man ändra själva anropet gör detta i Act-delen och behöver man ändra verifieringen av resultatet görs detta i Assert-delen.

En annan fördel är att man inte frestas att göra "flera tester i ett test". Dvs först testa en sak och verifiera den, sedan testa en annan sak och verifiera den i samma test. Eller ännu värre: blanda flera testaanrop och verifieringar med varandra i en enda röra. Använder man Arrange - Act - Assert-mönstret så tvingas man att separera kod för testuppsättning, testanrop och verifikation. Man kan i och för sig fortfarande testa fler än en sak, men det kräver i så fall att man gör fler än ett anrop i Act-delen, och det har vi ju slagit fast ovan att man inte skall göra;)

Notera att exemplet ovan givetvis är ett mycket enkelt exempel och att de testerna man skriver i verkliga livet oftast är bra mycket mer komplicerade och ofta använder diverse mockramverk. Faktum kvarstår dock. Strukturerar man sina enhetstest enligt Arrange - Act - Assert-mönstret kan man öka läsbarheten och minska kostnaden för underhåll av testen. Inte så dumt.

Länkar: