Domanda

Voglio essere in grado di dividere una grande prova per i test più piccole in modo che quando i test più piccoli passano implicano che la grande prova sarebbe anche passare (quindi non c'è motivo di eseguire la grande prova originale). Io voglio fare questo perché i test più piccole di solito impiega meno tempo, meno sforzo e sono meno fragili. Vorrei sapere se ci sono dei test modelli di progettazione o di strumenti di verifica che possono aiutare me per raggiungere questo test scissione in modo robusto.

temo che il collegamento tra le prove più piccole e la prova originale viene perso quando qualcuno cambia qualcosa nella serie di test più piccoli. Un altro timore è che la serie di test più piccoli in realtà non copre la grande prova.

Un esempio di quello che sto puntando a:

//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);
}
È stato utile?

Soluzione

Il primo suggerimento che farò è quella di ri-factor i test sul rosso (non). Per farlo, dovrete rompere il vostro codice di produzione temporanea. In questo modo, si sa i test sono ancora validi.

Uno schema comune è quello di utilizzare un dispositivo di prova separata per la raccolta di prove "grandi". Non è necessario attenersi alle "tutti i test per una classe in classe un test" modello. Se un una serie di test sono legati gli uni agli altri, ma sono estranei a un'altra serie di test, poi metterli nella loro stessa classe.

Il più grande vantaggio di utilizzare una classe separata per contenere i singoli piccoli test per la grande prova è che si può usufruire di setup e metodi di lacrime verso il basso. Nel tuo caso, vorrei spostare le linee che hanno commentato con:

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

per il metodo di impostazione (in JUnit, un metodo annotato con @Before). Se si dispone di alcune operazioni di configurazione insolitamente costoso che deve essere fatto, la maggior parte dei framework di test xUnit avere un modo per definire un metodo di configurazione che viene eseguito una volta prima di tutti i test. In JUnit, questo è un metodo public static void che ha l'annotazione @BeforeClass.

Se i dati di test è immutabile, tendo a definire le variabili come costanti.

Mettere insieme tutto questo, si potrebbe avere qualcosa di simile:

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);
    }

}

Altri suggerimenti

Tutto quello che posso suggerire è il libro xUnit prova . Se c'è una soluzione dovrebbe essere lì.

theBigTest manca la dipendenza B. Anche deride smallerTest1 B dipendenza. In smallerTest2 si dovrebbe prendere in giro InputFromA.

Perché avete creato un grafico delle dipendenze come hai fatto?

A prende un B poi quando A::process Input, è quindi processo di post InputFromA in B.

Mantenere la grande prova e refactoring A e B per cambiare la mappatura delle dipendenze.

[EDIT] in risposta alle osservazioni.

@mkorpela, il mio punto è che, guardando il codice e le loro dipendenze è come si inizia ad avere un'idea di come creare test più piccoli. A ha una dipendenza da B. In modo che esso per completare il suo process() deve utilizzare B di process(). A causa di questo, B ha una dipendenza A.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top