2010-06-21 17 views
15

Редактировать: Не доступно JUnit 4 на данный момент.JUnit Exception Testing

Привет,

У меня есть вопрос о «умных» тестирования исключение с JUnit. В это время, я делаю это так:

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

    } 

Как вы можете видеть, мне нужен один TRY/поймать блок для каждой функции, которая должна бросить исключение. Кажется, это не лучший способ сделать это - или нет возможности уменьшить использование try/catch?

+0

Я хотел бы предложить не делать System.out.println внутри теста. System.out и System.err имеют тенденцию смешивать и производить искаженный результат теста. – RockyMM

ответ

31

Предлагаю вам разбить testGet на несколько отдельных тестов. Отдельные блоки try/catch кажутся довольно независимыми друг от друга. Вы также можете извлечь общую логику инициализации в свой собственный метод настройки.

После того, как вы едите, что вы можете использовать поддержку аннотаций исключения JUnit4, в что-то вроде этого:

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

Мне это нравится, но я боюсь перейти на JUnit4 в запущенном проекте. – InsertNickHere

+4

@InsertNickHere: Ваша осторожность заслуживает похвалы, но я предлагаю неуместно в этом случае. JUnit4 может запускать тесты в стиле JUnit3 без изменений, позволяя вам постепенно переносить один тест за раз. Кроме того, JUnit4 сейчас 4 года, на самом деле вам пора. – skaffman

+1

Я должен полностью согласиться с вами - в этом случае мне нужно перейти на JUnit 4. – InsertNickHere

3

С JUnit 4 вы можете использовать аннотации. Тем не менее, вы должны разделить свой тест на 3 различных метода, чтобы это работало чисто. Обратите внимание, что IMHO, улавливающее исключение в первом сценарии, должен быть неудачным, поэтому я соответствующим образом изменил блок catch.

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

Как я уже говорил выше, мне нравится идея, но я могу переключиться на JUnit4 в запущенном проекте. – InsertNickHere

+0

@InsertNickHere, вы должны разбить свой тест на 3 отдельных метода в любом случае, с общей настройкой. Затем, если вы действительно хотите свести к минимуму код обработки исключений (и у вас есть много дублирующего кода, как те, которые вы показываете), вы можете извлечь try/catch в отдельный метод и передать фактический метод для тестирования через интерфейс (но это действительно чрезмерное ИМХО, и это затрудняет понимание вашего тестового кода). –

12

Если у вас есть ожидаемое исключение, и вы не можете использовать аннотацию в ловушку его, вам нужно, чтобы поймать его и утверждают, что у вас есть то, что вы ожидали. Например:

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

Если вы можете использовать аннотацию вместо этого, сделайте это, поскольку это намного яснее. Но это не всегда возможно (например, потому что вы тестируете последовательность методов или потому, что используете JUnit 3).

+0

Извините, но я не вижу никакого преимущества вашей версии по сравнению с моей. Может быть, нет JUnit3 :( – InsertNickHere

+1

Вы слишком много вкладываете в тестовый файл. Вы слишком много вкладываете в «попытку». Вы печатаете сообщения для ручного чтения, а не делаете утверждения теста неудачными!(Вы внедряете непереносимые пути в свой код, но это ваш бизнес ...) –

+0

Пути только для этого примера, я не буду отправлять 100% исходный код здесь. Но спасибо за этот совет. :) – InsertNickHere

2

Наиболее краткий синтаксис предоставленного 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

В Java 8, вы можете используйте лямбда-выражения, чтобы получить более жесткий контроль над тем, когда вызывается исключение. Если вы используете метод аннотаций, вы только утверждаете, что исключение выбрано где-то в методе тестирования. Если вы выполняете более одной строки кода в тесте, вы рискуете пройти тест, когда он потерпит неудачу. Решение Java 8 - это что-то вроде этого.

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

Использование:

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