Тестирование Android JUnit… Как ожидать исключения

#android #testing #junit

#Android #тестирование #junit

Вопрос:

Я пытаюсь написать несколько тестов, используя встроенную платформу тестирования Android Junit. Я столкнулся с проблемой с тестом, в котором я ожидаю, что будет выдано исключение. В JUnit аннотация для метода тестирования будет:

@Test(expected = ArithmeticException.class)

Однако в Android этот тест завершается с ошибкой ArithmeticException.

Я понимаю, что реализация Android является лишь подмножеством JUnit 3 и даже не допускает аннотацию @Test (должна быть @SmallTest, @MediumTest или @LargeTest, и ни один из них не допускает параметр ‘expected = ..’), но это кажется довольно значительным тестом, и похоже, что фреймворку тестирования Android серьезно не хватало бы, если бы у него не было этой функции.

Примечание: Я протестировал это, добавив JUnit jar в проект и добавив аннотации к моим методам тестирования. Для меня имеет смысл, почему аннотации будут полностью проигнорированы, потому что платформа Android (runner?) не ищет эту аннотацию и просто игнорирует ее. В принципе, я просто ищу «правильный» способ сделать это в рамках фреймворка.

Ответ №1:

Стандартная идиома junit 3 для такого рода тестов была:

 public void testThatMethodThrowsException()
{
  try
  {
    doSomethingThatShouldThrow();
    Assert.fail("Should have thrown Arithmetic exception");
  }
  catch(ArithmeticException e)
  {
    //success
  }
}
  

Комментарии:

1. Эта идиома также работает в JUnit4, когда вы хотите проверить состояние чего-либо после возникновения исключения. Вы не можете ничего утверждать при использовании «ожидаемой» идиомы после возникновения исключения.

2. Вы должны отменить пометку этого ответа как правильного и проверить ответ sandrstar, который является лучшим способом.

Ответ №2:

Теперь JUnit4 доступен через Android SDK (см. android-test-kit)

Обновление: теперь оно официально на d.android.com:

AndroidJUnitRunner — это новый разделенный тестовый раннер для Android, который является частью библиотеки тестов поддержки Android и может быть загружен через репозиторий поддержки Android. Новый раннер содержит все улучшения GoogleInstrumentationTestRunner и добавляет больше функций:

  • Поддержка JUnit4
  • Реестр инструментария для доступа к инструментам, контексту и аргументам пакета
  • Тестовые фильтры @SdkSupress и @RequiresDevice
  • Тайм-ауты тестирования
  • Сегментирование тестов
  • Поддержка RunListener для подключения к жизненному циклу тестового запуска
  • Механизм мониторинга активности ActivityLifecycleMonitorRegistry

Итак, тестирование исключений в стиле JUnit4 с использованием ожидаемой аннотации:

 @Test(expected= IndexOutOfBoundsException.class) 
public void empty() { 
     new ArrayList<Object>().get(0); 
}
  

или ожидаемые правила исключения:

 @Rule
public ExpectedException thrown = ExpectedException.none();

@Test
public void shouldTestExceptionMessage() throws IndexOutOfBoundsException {
    List<Object> list = new ArrayList<Object>();

    thrown.expect(IndexOutOfBoundsException.class);
    thrown.expectMessage("Index: 0, Size: 0");
    list.get(0); // execution will never get past this line
}
  

также возможно.

Обратитесь к официальной документации для получения более подробной информации о том, как настроить библиотеку поддержки тестирования.

Комментарии:

1. Работает ли это у вас с модульными тестами? т. е. в тестовом каталоге проекта, а не в каталоге androidTest?

2. @Louth да, на самом деле я использую его в основном без gradle, и большую часть времени он работает нормально. Я не пробовал eclipse, но IntelliJ Idea подходит. Вы можете использовать maven или просто получить jar из вашего Android SDK (распакуйте aar из extras android m2repository com android support test testing-support-lib .1).

3. Да, приветствую @sandrstar. Это довольно старая тема, и да, при использовании Android Studio теперь все довольно просто. 🙂

Ответ №3:

Я искал несколько хороших решений, однако ни одно из решений меня по-настоящему не удовлетворило. Итак, я создал свой собственный.

 public final void assertThrows(VoidFunction v, Class<? extends Exception> e) {
    try {
        v.call();
    } catch (Exception ex) {
        if (!ex.getClass().equals(e)) {
            Assert.fail();
        }
        // Do nothing, basically succeeds test if same exception was thrown.
        return;
    }

    // Fails if no exception is thrown by default.
    Assert.fail();
}
  

Где VoidFunction — это простой интерфейс:

 @FunctionalInterface
public interface VoidFunction {
    void call();
}
  

Это используется следующим образом (например):

 @Test
public void testFoo() {
    assertThrows(() -> foo.bar(null)), NullPointerException.class);
    assertThrows(() -> foo.setPositiveInt(-5)), IllegalArgumentException.class);
    assertThrows(() -> foo.getObjectAt(-100)), IndexOutOfBoundsException.class);
    assertThrows(new VoidFunction() {
            @Override
            public void call() {
                foo.getObjectAt(-100);
            }
        }, IndexOutOfBoundsException.class); // Success
    assertThrows(new VoidFunction() {
                @Override
                public void call() {
                    throw new Exception();
                }
            }, NullPointerException.class); // Fail

}
  

Я включил один вызов без использования лямбда-выражения, это иногда облегчает понимание кода, по крайней мере, для меня.
Простой в использовании и допускающий несколько перехватов исключений ОДНИМ методом.