2010-06-21 19 views
15

Edit: No JUnit 4 disponible en este momento.JUnit Exception Testing

Hola,

Tengo una pregunta acerca de las pruebas excepción "inteligente" con JUnit. En este momento, lo hago de esta manera:

public void testGet() { 

    SoundFileManager sfm = new SoundFileManager(); 

     // Test adding a sound file and then getting it by id and name. 
     try { 
      SoundFile addedFile = sfm.addSoundfile("E:\\Eclipse_Prj\\pSound\\data\\Adrenaline01.wav"); 
      SoundFile sf = sfm.getSoundfile(addedFile.getID()); 
      assertTrue(sf!=null); 
      System.out.println(sf.toString()); 

      sf = sfm.getSoundfileByName("E:\\Eclipse_Prj\\pSound\\data\\Adrenaline01.wav"); 
      assertTrue(sf!=null); 
      System.out.println(sf.toString()); 
     } catch (RapsManagerException e) { 
      System.out.println(e.getMessage()); 
     } 

     // Test get with invalid id. 
     try { 
      sfm.getSoundfile(-100); 
      fail("Should have raised a RapsManagerException"); 
     } catch (RapsManagerException e) { 
      System.out.println(e.getMessage()); 
     } 

     // Test get by name with invalid name 
     try { 
      sfm.getSoundfileByName(new String()); 
      fail("Should have raised a RapsManagerException"); 
     } catch (RapsManagerException e) { 
      System.out.println(e.getMessage()); 
     } 

    } 

Como se puede ver, necesito bloque/captura de un intento para cada función que se supone que es una excepción. Parece que no es una buena forma de hacerlo, ¿o no hay posibilidad de reducir el uso de try/catch?

+0

Yo sugeriría en contra de hacer System.out.println dentro prueba. System.out y System.err tienden a mezclar y producir salida de prueba confusa. – RockyMM

Respuesta

31

Sugiero que debe dividir testGet en varias pruebas por separado. Los bloques de prueba/captura individuales parecen ser bastante independientes entre sí. Es posible que también desee extraer la lógica de inicialización común en su propio método de configuración.

Una vez que tenga eso, se puede utilizar el soporte excepción de anotación de junit4, algo como esto:

public class MyTest { 

private SoundManager sfm; 

@Before 
public void setup() { 
     sfm = new SoundFileManager(); 
} 

@Test 
public void getByIdAndName() { 
    // Test adding a sound file and then getting it by id and name. 
     SoundFile addedFile =    
     sfm.addSoundfile("E:\\Eclipse_Prj\\pSound\\data\\Adrenaline01.wav"); 
     SoundFile sf = sfm.getSoundfile(addedFile.getID()); 
     assertTrue(sf!=null); 
     System.out.println(sf.toString()); 

     sf = sfm.getSoundfileByName("E:\\Eclipse_Prj\\pSound\\data\\Adrenaline01.wav"); 
     assertTrue(sf!=null); 
     System.out.println(sf.toString()); 
} 

@Test(expected=RapsManagerException.class) 
public void getByInvalidId() { 
     // Test get with invalid id. 
     sfm.getSoundfile(-100); 
} 

@Test(expected=RapsManagerException.class) 
public void getByInvalidName() { 
     // Test get with invalid id. 
     sfm.getSoundfileByName(new String()); 
} 
} 
+0

Me gusta este, pero me da miedo cambiar a JUnit4 en un proyecto en ejecución. – InsertNickHere

+4

@InsertNickHere: Su precaución es encomiable, pero sugiero que se extravió en este caso. JUnit4 puede ejecutar pruebas estilo JUnit3 sin cambios, lo que le permite migrar gradualmente una prueba a la vez. Además, JUnit4 ahora tiene 4 años, es hora de que te mudes. – skaffman

+1

Tengo que estar totalmente de acuerdo contigo, en este caso tengo que moverme a JUnit 4. – InsertNickHere

3

Con JUnit 4, puede usar anotaciones en su lugar. Sin embargo, debe separar su prueba en 3 métodos distintos para que esto funcione limpiamente. Tenga en cuenta que en mi humilde opinión, la captura de una excepción en el primer escenario debería ser un error, por lo que modifiqué el bloque catch en consecuencia.

public void testGet() { 
    SoundFileManager sfm = new SoundFileManager(); 

    // Test adding a sound file and then getting it by id and name. 
    try { 
     SoundFile addedFile = sfm.addSoundfile("E:\\Eclipse_Prj\\pSound\\data\\Adrenaline01.wav"); 
     SoundFile sf = sfm.getSoundfile(addedFile.getID()); 
     assertTrue(sf!=null); 
     System.out.println(sf.toString()); 

     sf = sfm.getSoundfileByName("E:\\Eclipse_Prj\\pSound\\data\\Adrenaline01.wav"); 
     assertTrue(sf!=null); 
     System.out.println(sf.toString()); 
    } catch (RapsManagerException e) { 
     fail(e.getMessage()); 
    } 
} 

@Test(expected=RapsManagerException.class) 
public void testGetWithInvalidId() { 
    SoundFileManager sfm = new SoundFileManager(); 

    sfm.getSoundfile(-100); 
} 

@Test(expected=RapsManagerException.class) 
public void testGetWithInvalidName() { 
    SoundFileManager sfm = new SoundFileManager(); 

    sfm.getSoundfileByName(new String()); 
} 
+0

Como dije antes, me gusta la idea, pero me cuesta cambiar a JUnit4 en un proyecto en ejecución. – InsertNickHere

+0

@InsertNickHere, debe dividir su prueba en 3 métodos individuales de todos modos, con la configuración común. Entonces, si realmente quiere minimizar el código de manejo de excepciones (y tiene muchos códigos duplicados como los que muestra) puede extraer el try/catch en un método separado, y pasar el método real para ser probado a través de una interfaz (pero esto es realmente exagerado en mi humilde opinión, y hace que tu código de prueba sea más difícil de entender). –

12

Si usted tiene una excepción esperada y no se puede utilizar una anotación de atraparla, tiene que atraparla y afirmar que tienes lo que se esperaba. Por ejemplo:

Throwable caught = null; 
try { 
    somethingThatThrows(); 
} catch (Throwable t) { 
    caught = t; 
} 
assertNotNull(caught); 
assertSame(FooException.class, caught.getClass()); 

Si puede usar una anotación, hágalo ya que está mucho más claro. Pero eso no siempre es posible (p. Ej., Porque estás probando una secuencia de métodos o porque estás usando JUnit 3).

+0

Lo siento, pero no veo ninguna ventaja de su versión en comparación con la mía. Tal vez no haya ninguno con JUnit3 :( – InsertNickHere

+1

Estás poniendo demasiado en un caso de prueba. Estás poniendo demasiado en un "intento". Estás imprimiendo mensajes para lectura manual en lugar de hacer que las afirmaciones de prueba fallen.(Está incrustando rutas no portátiles en su código, pero esa es su tarea ...) –

+0

Las rutas son solo para este ejemplo, no voy a publicar código 100% original aquí. Pero gracias por ese consejo de todos modos. :) – InsertNickHere

2

La sintaxis más concisa es proporcionada por catch-exception:

public void testGet() { 
    SoundFileManager sfm = new SoundFileManager(); 
    ... // setup sound file manager 

    verifyException(sfm, RapsManagerException.class) 
     .getSoundfile(-100); 

    verifyException(sfm, RapsManagerException.class) 
     .getSoundfileByName(new String()); 
} 
0

En Java 8, puede use expresiones lambda para obtener un control más estricto sobre cuándo se lanza la excepción. Si usa el método de anotaciones, solo está afirmando que la excepción se emite en algún lugar del método de prueba. Si está ejecutando más de una línea de código en la prueba, corre el riesgo de que su prueba pase cuando debería fallar. La solución Java 8 es algo como esto.

static void <T extends Exception> expectException(Class<T> type, Runnable runnable) { 
    try { 
     runnable.run() 
    } catch (Exception ex) { 
     assertTrue(ex.getClass().equals(type)); 
     return; 
    } 
    assertTrue(false); 
} 

Uso:

@Test 
public void test() 
    MyClass foo = new MyClass(); 
    // other setup code here .... 
    expectException(MyException.class,() -> foo.bar()); 
}