Organisation von Junit Testfällen?

  • Themenstarter Themenstarter Testdrifen
  • Beginndatum Beginndatum
T

Testdrifen

Gast
Hi,

ich bin neuerdings dabei Unit-Tests zu schreiben und bin noch etwas unsicher wie so eine unit-test klasse aussehen sollte.
Ich hab in der regel mehrere komponenten die ich im vorfeld zusammenbauen muss, damit eine Komponente funktioniert.
Wenn ich jetzt mehrere Dinge in einer Testklasse testen lassen will, muss ich ggf. die Dinge die ich in der setUp methode eingerichtet habe umändern. Mittelfristig führt das zu einem heiden Chaos und keiner blickt mehr durch, welche der dutzenden hilfsmethoden jetzt für welchen testfall zuständig ist.
Dinge wie Mockito haben das schon stark entschärft, aber das generelle Problem bzw. mein Eindruck dass das sehr chaotisch ist bleibt.
Wie macht ihr das? Jedes Feature einer Klasse ist eine eigene Testfallklasse, wenn die setUp Bedinungen nicht gleich sind? Oder wie organisiert ihr eure Testfälle?
 
Ich hab in der regel mehrere komponenten die ich im vorfeld zusammenbauen muss, damit eine Komponente funktioniert.

Damit hast du keine Unittests mehr, sondern Integrationstests.

Wenn ich jetzt mehrere Dinge in einer Testklasse testen lassen will, muss ich ggf. die Dinge die ich in der setUp methode eingerichtet habe umändern. Mittelfristig führt das zu einem heiden Chaos und keiner blickt mehr durch, welche der dutzenden hilfsmethoden jetzt für welchen testfall zuständig ist.
Dinge wie Mockito haben das schon stark entschärft, aber das generelle Problem bzw. mein Eindruck dass das sehr chaotisch ist bleibt.

Meiner Meinung nach, ist der Aufwand den man betreiben muss, um Tests zu schreiben, ein gutes Maß für Code Qualität. Je geringer der Aufwand ist, desto besser ist der Code.

Das was du beschreibst klingt für mich so: Du hast eine Klasse A dieses hängt von Klasse B ab, Klasse B hängt wiederum von Klasse C ab. Je nach dem welchen Zustand jetzt Klasse C hat, mach Klasse B irgendetwas, davon hängt nun das Verhalten von Klasse A ab. Dadurch hat A eine Abhängigkeit auf C. Bei dir wird das wahrscheinlich noch ein paar Ebenen weiter gehen.

Wenn jetzt B ein Interface wäre, könntest du dir den ganzen Rattenschwanz den B als Klasse mitbringt sparen, und für deinen Test eine Mock-Implementierung vom Interface B schreiben, diese könntest du dann so schreiben dass du ganz einfach alle Pfade der Klasse A abdecken kannst. Zum Beispiel so:

Java:
public class A{
    private B b;
    
    public A(B b){
         this.b = b;
    }

   public int foo(){
       if(b.bar())
          return 1;
       else
           return 0;
   }
}

Java:
public interface B{  
   boolean bar();
}

Java:
public class RealB implements B{
   
   public boolean bar(){
          //Irgendetwas total kompliziertes, das noch von anderen Sachen abhängt, und am Ende entweder true oder false zurück gibt 
   }
}

Java:
public class BTestImpl implements B{
   private boolean bar;

   public BTestImpl(boolean bar){
      this.bar = bar;
   }

   public boolean bar(){
       return bar;
   }
}

Java:
public class ATest{
  
   @Test
   public void testFoo(){
       B b = new BTestImpl(true);
       A a = new A(b);
       assertEquals(1, a.foo());

       b = new BTestImpl(false);
       a = new A(b);
       assertEquals(0,a.foo());
   }
}

Unittest heißt, dass man genau eine Unit testet. Mit einer Unit ist in der Regel eine Komponente gemeint. Die "Kunst" ist eine Komponente so zu isolieren, dass man diese einzeln testen kann. Wenn man so weit ist, dann stellt sich deine Frage gar nicht. Daher schreibt man übrigens auch besseren Code, wenn man Test-Driven arbeitet, da man sich die Gedanken, wie man den Code aufteilt, schon vorher macht.

Wenn das Kind schon in den Brunnen gefallen ist, muss man solche Tests schreiben, wie du sie beschreibst. Diese Tests sind aber ein super Mittel um deinen Code zu refactoren. Damit kannst du sicherstellen, dass dein Code nach dem Refactoring noch genau so funktioniert wie davor. Wenn du damit fertig bist, sind sowohl die Tests als auch der Code, besser.
 
Hi,

danke für die Antwort. Ich stehe noch sehr am Anfang von dem was ich machen will, deswegen ist es noch nicht zu spät 🙂

Hier mal eine Test für eine Filterklasse die Sätze rausfiltern soll in denen gewissen Wörter auftauchen. Die Klasse kann ich nicht testen ohne Sätze bestehend aus token bereitzustellen. Benutzt hab ich dafür hier Mockito - ist das jetzt noch ein unit-test oder schon ein integrationstest? Anders gefragt, wenn ich das filtern als unit-test testen wollte, wie müsste das grob aussehen?

Java:
import java.util.LinkedList;
import java.util.List;

import org.junit.Before;
import org.junit.Test;

import static org.junit.Assert.*;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

public class TestFIBQFilter {

    List<FilterRule> rules;
    private LinkedList<Sentence> sentences;

    @Test
    public void getFilteredSentences() {
        Filter filter = new Filter(rules.toArray(new FilterRule[0]));
        List<Sentence> filtered = filter.applyFilters(sentences);
        assertEquals(1, filtered.size());
        assertEquals(6, filtered.get(0).getTokens().size());
    }

    @Before
    public void setUp() {
        initSentence();
        initFilterRules();
    }

    private void initSentence() {
        List<Token> tokens = initTokens();
        Sentence sentence = mock(Sentence.class);
        when(sentence.getTokens()).thenReturn(tokens);
        sentences = new LinkedList<Sentence>();
        sentences.add(sentence);
    }

    private List<Token> initTokens() {
        Token token1 = mock(Token.class);
        when(token1.getForm()).thenReturn("How");
        Token token2 = mock(Token.class);
        when(token2.getForm()).thenReturn("are");
        Token token3 = mock(Token.class);
        when(token3.getForm()).thenReturn("you");
        Token token4 = mock(Token.class);
        when(token4.getForm()).thenReturn("feeling");
        Token token5 = mock(Token.class);
        when(token5.getForm()).thenReturn("today");
        Token token6 = mock(Token.class);
        when(token6.getForm()).thenReturn("?");
        List<Token> tokens = new LinkedList<Token>();
        tokens.add(token1);
        tokens.add(token2);
        tokens.add(token3);
        tokens.add(token4);
        tokens.add(token5);
        tokens.add(token6);
        return tokens;
    }

    private void initFilterRules() {
        FilterRule rule = mock(FilterRule.class);
        rules = new LinkedList<FilterRule>();
        rules.add(rule);
    }
}
 

Zurück
Oben