Tester for asynkron kode

Lenge siden jeg har hatt noe C#-kode på bloggen nå.., la meg gjøre noe med det. På jobben i forrige uke lagde vi to små extension methods jeg har lyst til å dele med verden. Det er ikke noe rocket science, men kan være greit om du har samme behovet som oss, og du ikke har tenkt på det selv.

Systemene vi jobber med inneholder MYE asynkronitet / samtidighet. Når vi skal lage automatiserte tester – spesielt integrasjonstester – må vi forholde oss til at vi ikke øyeblikkelig får det resultatet vi forventer (eventual consistency). En test kan f.eks. sende en melding som plukkes opp og behandles av en annen tråd. Dette fører til endel kompleksitet i testene; ofte legger vi inn en sleep før vi kontrollerer resultatet.

Her er en tenkt test hvor vi har en publisher og en subscriber. Vi publiserer en melding, og kontrollerer at subscriberen har mottatt:

 

2 [TestFixture]
3 public class When_publishing_a_message
4 {
5   [Test]
6   public void Subscriber_should_receive_within_reasonable_time()
7   {
8     When.publisher.distribute(a_message);
9     Thread.Sleep(2_seconds); // 2 sec seems to be the worst case
10     Then.subscriber.ReceivedMessageCount.ShouldEqual(1);
11   }
12
13   private Publisher publisher;
14   private Subscriber subscriber;
15   private Message a_message;
16   private const int 2_seconds = 2000;
17
18   /* Setup, Teardown and BDD snippet not shown */
19 }

Ett av problemene med dette er at faktorer utenfor vår kontroll kan føre til at tiden vi må vente av og til øker ganske mye. Vi bruker da endel tid på å finne den optimale ventetiden, for vi vil ikke at testene skal bruke for lang tid. Men fra tid til annen feiler likevel en og annen test pga. stor belastning på bygg-serveren. Dette er ikke bra – vi må kunne stole på stabiliteten på builden!

For å bedre dette har vi kommet opp med en abstraksjon vi kan bruke i stedet for en hardkodet sleep. Disse extensionmetodene sjekker om et predikat er tilfredstilt helt til det er det, eller til vi har nådd en timeout-verdi:

 

1 public static class AsyncTestExtensions
2 {
3   public static void ShouldBeTrueWithin(
4       this Func<bool> predicate, int milliseconds)
5   {
6     DateTime timeout = DateTime.Now.AddMilliseconds(milliseconds);
7     while (DateTime.Now < timeout)
8     {
9       if (predicate()) return;
10       Thread.Sleep(10);
11     }
12     Assert.Fail();
13   }
14  
15   public static void ShouldBeFalseWithin(
16       this Func<bool> predicate, int milliseconds)
17   {
18     ShouldBeTrueWithin(() => !predicate(), milliseconds);
19   }
20 }

Med disse på plass kan vi skive om testen slik:

 

22 [TestFixture]
23 public class When_publishing_a_message
24 {
25   [Test]
26   public void Subscriber_should_receive_within_reasonable_time()
27   {
28     When.publisher.distribute(a_message);
29     Then.subscriber_have_received_a_message.ShouldBeTrueWithin(2_seconds);
30   }
31
32   private Func<bool> subscriber_have_received_a_message = () =>
33     subscriber.ReceivedMessageCount.Equals(1);
34
35   /* Fields, Setup, Teardown and BDD snippet not shown */
36   /* Note : subscriber now needs to be static          */
37 }

Testen er nå mye mere lesbar (i min mening). I tillegg kjører testene gjevnt over raskere, men uten å feile de gangene maskinen trenger litt mere tid på å gjøre seg ferdig.

PS: When og Then-nøkkelordene i testene stammer fra min Ultra-tiny given-when-then DSL-snippet. ShouldEqual-metoden i den første versjonen av testen kommer fra coreTDD, som vi bruker i alle tester i stedet for vanlige nUnit asserts.

Kategorier: Testing / TDD.
RSS feed for kommentarene. Tilbaketråkk.

Skriv en kommentar

Tillatte tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>


Einar W. Høst: Det er jo læringen som gjør det morsomt! Se også http://norvig.com/21-days...

Pagliacci: OBS! tl;wr. Det er vel akuratt det jeg sliter med med min læring innenfor pr...

Torbjørn: La oss anta to ulike definisjoner av Template Method pattern - de to ytterpunkte...

Lars-Petter: Hei igjen. Siden du inviterer til å ta diskusjonen i bloggen, og har tatt deg t...

Torbjørn: Lars-Petter: Det er én måte å se det på. Det er absolutt fortsatt Template M...

Lars-Petter: Hei. Har du ikke i prinsippet her gått over fra Template Method (arv) til Strat...

Christian Abildsø: I alle fall i C#, så føles dette som kode som blir mer fleksibel men vanskelig...

Torbjørn: Hei Henrik, og takk for tilbudet. Ble oppmerksom på Rasberry Pi for under en uk...

Henrik Sandaker Palm: Ang. større hobby prosjekt. Du er som er en slik rakker på programmering har j...

Øivind Nilsen: Slutt å bruke mobilnummeret mitt som eksempel !...

Mulig relaterte linker

 Hold deg oppdatert

Søk i bloggen

Ferske innlegg

  • En historie om programmering
  • Template Method del 4: Multippel arv
  • Template Method Intermesso
  • Template Method del 3: Bare funksjoner
  • Kategorier

  • .net ninja (37)
  • Bøker (17)
  • Diverse prosjekter (35)
  • DSL (10)
  • Erlang (10)
  • F# (5)
  • Hardware (1)
  • Jobb (77)
  • Julekalender (51)
  • kjempekjekt.com (23)
  • LISP/Clojure (33)
  • NNUG / community (60)
  • O/RM & databaser (10)
  • Off topic (116)
  • OO-design/clean code (30)
  • Podcasts (14)
  • Polyglot (77)
  • Ruby (27)
  • Silverlight / RIA (3)
  • Software/verktøy (20)
  • Softwareutvikling (21)
  • Testing / TDD (30)
  • the contiki strip (13)
  • User experience (3)
  • WCF (3)
  • Webutvikling (32)
  • WPF (9)
  • WTF (12)
  • Last ned Wallpaper

    Programmeringsbloggens tøffe skrivebordsbakgrunn med snippets fra ulike språk laster du ned her!

    Abonner via epost

    Om du vil kan du få alle nye blogposter tilsendt til din epost. Abonner nå, det er kjempeenkelt!

    Meta