[Dutch] Geautomatiseerd Testen Met Erlang/OTP en Travis-CI - Een Introductie.

written in Erlang

Erlang/OTP is ontworpen voor het bouwen van grote, schaalbare, soft-realtime systemen met een hoge beschikbaarheid. Het testen van dergelijke systemen is niet eenvoudig, laat staan geautomatiseerd testen. Voor Erlang zijn er dan ook geavanceerde automatische test methoden beschikbaar.

De drie belangrijkste methoden worden hier kort besproken aan de hand van een test project. De methoden zijn:

Je cloned het project van Github met het volgende commando:

1
$ git clone git@github.com:wardbekker/ci_quickstart.git

Voor het compileren van het project en uitvoeren van de testen gebruik je Rebar, een sophisticated build-tool for Erlang projects that follows OTP principles. Je bouwt Rebar als volgt:

1
2
3
4
5
6
7
8
9
10
$ git clone git://github.com/basho/rebar.git
$ cd rebar
$ ./bootstrap
Recompile: src/getopt
...
Recompile: src/rebar_utils
==> rebar (compile)
Congratulations! You now have a self-contained script called "rebar" in
your current working directory. Place this script anywhere in your path
and you can use rebar to build OTP-compliant apps.

Unit testing met EUnit

Je begint met de eenvoudigste test methode; EUnit. Dit is een unit testing bibliotheek voor Erlang. In een unit test controleer je of de functie goed werkt bij bekende input en resultaat. In dit voorbeeld heb je de functie addition geimplementeerd in de module ci_quickstart_math en twee assertions. (Je voert deze test uit op de commandline met: rebar get-deps compile eunit):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
-module(ci_quickstart_math).
-export([addition/2]).

-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-endif.

addition(X, Y) ->
    X + Y.

-ifdef(TEST).

simple_test() ->
    ?assertEqual(4, addition(2,2)),
    ?assertNotEqual(3, addition(1,1)).

-endif.

Het slechte nieuws is dat de waarde van deze test zeer laag is. Weet je nu zeker dat optelling goed gaat in alle gevallen? Het enige wat de test nu aantoont is dat:

  • addition(2,2) == 4
  • addition(1,1) /= 3

Stel, je verandert de implementatie van de function addition in:

1
2
 addition(X, Y) ->
    4.

De testen slagen in dit geval, maar dit betekend niet dat de implementatie van addition correct is.

Sterker nog; De argumenten zijn in dit geval 64-bit small integers, en die hebben een bereik van -576460752303423489 t/m 576460752303423488. Met twee argumenten, betekend dit dat er enorm veel verschillende input mogelijk is. En in de unit test controleer je er maar 3!?!? Ook al ben je een harde werker en test je wel 10! addities, in feite is de waarde van de unit test niet verbeterd en nog steeds erg laag.

Wat nu?

QuickCheck

Wat je eigenlijk wil is een test methode dat alle mogelijke input variaties genereerd en de bijbehorende output controleert. Deze methode heet QuickCheck. Voor Erlang zijn er een aantal QuickCheck frameworks beschikbaar:

Een Quickcheck test voor de addition functie:

1
2
3
4
5
6
prop_sum() ->
    ?FORALL(
        {X, Y},
        {int(), int()},
        addition(X,Y) - Y == X
    ).

Voer de test uit door de Erlang Shell op te starten met ./shell.sh en de volgende functie aanroep proper:quickcheck(ci_quickstart_math:prop_sum()).

Specifieke nummers worden niet getest. Je gaat nu controleren of de functie voldoet aan de eigenschap dat als je Y weer er afhaalt, je X overhoud.

{int(), int()} genereerd tuples met twee random integers. De tuple wordt gebonden aan {X, Y} door pattern matching. Standaard worden er 100 combinaties getest, en dit aantal voer je op met de numtests optie: proper:quickcheck(ci_quickstart_math:prop_sum(),[{numtests,10000}])..

De uitdaging bij het werken met QuickCheck is het bedenken van de eigenschappen van de functie. Dit is lastiger dan het maken van een unit test. Sterker nog, het schrijven van de functie is vaak nog eenvoudiger dan het redeneren over de eigenschappen. Het positieve effect van QuickCheck op de kwaliteit van je code, en de manier waarop je als developer over je code nadenkt maakt deze tool een zeer waardevol onderdeel van je test gereedschapskist.

Common Test

Zoals bekend is Erlang uitermate geschikt voor het bouwen van concurrent, distributed en fault tolerant systemen. Om te controleren of je systeem werkt zoals verwacht, is complex.

Hiervoor is Common Test in het leven geroepen. Dit krachtige test framework is uitermate geschikt voor de ontwikkeling van pittige systeem tests. De inherente complexiteit van concurrent, distributed en fault tolerant systemen maakt Common Test complex. Hoe je een serieuze OTP applicatie op de pijnbank legt met CT valt derhalve buiten de scope van deze blogpost. Hier onder wel een minimaal Comment Test waarin de EUnit testen worden nagebootst door gebruik van pattern matching.

1
2
3
4
5
6
7
8
9
10
11
12
13
-module(basic_SUITE).
-include_lib("common_test/include/ct.hrl").

-export([all/0]).
-export([test1/1, test2/1]).

all() -> [test1,test2].

test1(_Config) ->
    3 = ci_quickstart_math:addition(1,2). %% validated using pattern matching

test2(_Config) ->
    2 = ci_quickstart_math:addition(1,1).  %% validated using pattern matching

Continuous integration met Travis-CI

Stel, je hebt een flinke hoeveelheid Eunit, Common Test en Quickcheck testen geïmplementeerd. Het uitvoeren van alle geavanceerde testen duurt lang en belast je systeem fors. Om deze, en nog meer goede redenen, is Continuous integration aan te raden.

Er zijn legio systemen waarmee het mogelijk is om continuous integration voor Erlang op te zetten. In dit voorbeeld gebruik je het hosted systeem Travis-CI. Deze dienst ondersteunt Erlang, integreert met het populaire Github en zorgt voor een vliegende start. En het is gratis voor open source projecten.

Voorbereiding

Het build proces van Travis-CI configureer je via het .travis.yml-bestand in de root van je repository. Een voorbeeld:

1
2
3
4
5
6
7
language: erlang // De repository bevat een Erlang project
notifications:
  email: you@example.org // Build success en failures stuurt Travis-CI naar dit adres.
otp_release: // Travis-CI test/bouwt je project voor meerdere Erlang/OTP versies.
  - R15B01
  - R15B
  - R14B04

Travis-CI Setup

Deze video toont hoe je start met Travis-CI:

  • Log in met je Github account.
  • Ga naar de Travis-CI profile pagina.
  • Schakel de gewenste Github repository in.

That’s it!

Setup

Travis-CI Success Run

Deze video toont hoe Travis-CI een geslaagde integration build rapporteerd:

Success

Travis-CI Failure Run

Deze video toont hoe Travis-CI een mislukte integration build rapporteerd:

Fail

Als je e-mail adres in .travis.yml staat, krijg je ook een e-mail notificatie dat de laatste commit de build gebroken heeft:

Broken build e-mail notification

Als de fout verholpen is, krijg je de volgende e-mail als de build weer slaagt:

Fixed build e-mail notification


Comments