Frage

Ich mag einen großen Test für kleinere Tests aufteilen können, so dass, wenn die kleineren Tests bestehen sie implizieren, dass der große Test auch passieren würde (so gibt es keinen Grund, den ursprünglichen großen Test auszuführen). Ich möchte, dies zu tun, weil kleinere Tests in der Regel weniger Zeit in Anspruch nehmen, weniger Aufwand und weniger zerbrechlich. Ich möchte wissen, ob es Testentwurfsmuster oder Verifizierungs-Tools, die mir helfen können, diesen Test Aufspaltung in einer robusten Art und Weise zu erreichen.

Ich fürchte, dass die Verbindung zwischen den kleineren Tests und dem ursprünglichen Test verloren geht, wenn jemand etwas in der Reihe von kleineren Tests ändert. Eine weitere Befürchtung ist, dass der Satz von kleineren Tests nicht wirklich den großen Test abdecken.

Ein Beispiel dessen, was ich bin mit dem Ziel an:

//Class under test
class A {

  public void setB(B b){ this.b = b; }

  public Output process(Input i){
    return b.process(doMyProcessing(i));
  }

  private InputFromA doMyProcessing(Input i){ ..  }

  ..

}

//Another class under test
class B {

   public Output process(InputFromA i){ .. }

  ..

}

//The Big Test
@Test
public void theBigTest(){
 A systemUnderTest = createSystemUnderTest(); // <-- expect that this is expensive

 Input i = createInput();

 Output o = systemUnderTest.process(i); // <-- .. or expect that this is expensive

 assertEquals(o, expectedOutput());
}

//The splitted tests

@PartlyDefines("theBigTest") // <-- so something like this should come from the tool..
@Test
public void smallerTest1(){
  // this method is a bit too long but its just an example..
  Input i = createInput();
  InputFromA x = expectedInputFromA(); // this should be the same in both tests and it should be ensured somehow
  Output expected = expectedOutput();  // this should be the same in both tests and it should be ensured somehow

  B b = mock(B.class);
  when(b.process(x)).thenReturn(expected);

  A classUnderTest = createInstanceOfClassA();
  classUnderTest.setB(b);

  Output o = classUnderTest.process(i);

  assertEquals(o, expected);
  verify(b).process(x);
  verifyNoMoreInteractions(b);
}

@PartlyDefines("theBigTest") // <-- so something like this should come from the tool..
@Test
public void smallerTest2(){
  InputFromA x = expectedInputFromA(); // this should be the same in both tests and it should be ensured somehow
  Output expected = expectedOutput();  // this should be the same in both tests and it should be ensured somehow

  B classUnderTest = createInstanceOfClassB();

  Output o = classUnderTest.process(x);

  assertEquals(o, expected);
}
War es hilfreich?

Lösung

Der erste Vorschlag, dass ich machen werde, ist zu re-Faktor auf rotem Ihre Tests (Fehler). Um dies zu tun, werden Sie Ihren Produktionscode vorübergehend brechen. Auf diese Weise wissen Sie die Tests sind nach wie vor gültig.

Ein übliches Muster ist eine separate Testvorrichtung pro Sammlung von „großen“ Tests zu verwenden. Sie müssen nicht „für eine Klasse in einer Testklasse alle Tests“ Muster auf den Stick. Wenn eine eine Reihe von Tests miteinander verwandt sind, aber zu einem anderen Satz von Tests nichts zu tun haben, dann setzen sie in ihrer eigenen Klasse.

Der größte Vorteil eine separate Klasse mit den einzelnen kleinen Tests für die große Bewährungsprobe zu halten ist, dass Sie die Vorteile der Einrichtung und tear-down Methoden erfolgen. In Ihrem Fall würde ich die Linien bewegen Sie haben gesagt:

// this should be the same in both tests and it should be ensured somehow

in das Setup-Verfahren (in JUnit, ein Verfahren mit @Before kommentiert). Wenn Sie etwas ungewöhnlich teuer Setup haben, dass getan werden muss, haben die meisten xUnit Test-Frameworks eine Möglichkeit, eine Setup-Methode, die ausgeführt wird, wenn vor allen Tests zu definieren. In JUnit, ist dies eine public static void Methode, die die @BeforeClass Annotation hat.

Wenn die Testdaten unveränderlich ist, neige ich dazu, die Variablen als Konstanten definiert werden.

Alle diese zusammen Einlochen, man könnte so etwas wie haben:

public class TheBigTest {

    // If InputFromA is immutable, it could be declared as a constant
    private InputFromA x;
    // If Output is immutable, it could be declared as a constant
    private Output expected;

    // You could use 
    // @BeforeClass public static void setupExpectations()
    // instead if it is very expensive to setup the data
    @Before
    public void setUpExpectations() throws Exception {
      x = expectedInputFromA();
      expected = expectedOutput();
    }

    @Test
    public void smallerTest1(){
      // this method is a bit too long but its just an example..
      Input i = createInput();

      B b = mock(B.class);
      when(b.process(x)).thenReturn(expected);

      A classUnderTest = createInstanceOfClassA();
      classUnderTest.setB(b);

      Output o = classUnderTest.process(i);

      assertEquals(o, expected);
      verify(b).process(x);
      verifyNoMoreInteractions(b);
    }

    @Test
    public void smallerTest2(){
      B classUnderTest = createInstanceOfClassB();

      Output o = classUnderTest.process(x);

      assertEquals(o, expected);
    }

}

Andere Tipps

Alles, was ich vorschlagen kann, ist das Buch xUnit Testmuster . Wenn es eine Lösung ist, sollte es da drin sein.

theBigTest fehlt die Abhängigkeit von B. Auch smallerTest1 Mocks B Abhängigkeit. In smallerTest2 sollten Sie InputFromA verspotten.

Warum haben Sie einen Abhängigkeitsgraphen wie Sie haben?

A nimmt einen B dann, wenn A::process Input Sie dann Post-Prozess InputFromA in B.

Halten Sie die große Bewährungsprobe und refactor A und B die Dependency Mapping zu ändern.

[EDIT] in Reaktion auf Bemerkungen.

@mkorpela, mein Punkt ist, dass auf dem Code und die Abhängigkeiten von der Suche ist, wie Sie beginnen eine Vorstellung davon zu bekommen, wie kleinere Tests zu erstellen. A hat eine Abhängigkeit von B. Damit es seine process() abzuschließen es B des process() verwenden. Aus diesem Grunde hat B eine Abhängigkeit von A.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top