Эксклюзивное тестирование JUnit

Изменить: не доступно JUnit 4 в это время.

Привет,

У меня вопрос о "умном" тестировании исключений с 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/catch для каждой функции, которая должна генерировать исключение. Кажется, это не лучший способ сделать это - или нет возможности уменьшить использование try/catch?

Ответы

Ответ 1

Я предлагаю вам разбить 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());
}
}

Ответ 2

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

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

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

Ответ 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());
}

Ответ 4

Самый сжатый синтаксис представлен 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());
}

Ответ 5

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