Лучший способ регистрации исключений при сбое тестов (например, использование правила junit) - PullRequest
5 голосов
/ 21 сентября 2011

Когда я запускаю полный набор тестов, было бы полезно, чтобы в моем журнале (SLF4J-) появлялись исключения, которые вызывали сбой теста.Каков наилучший метод для достижения этой цели?

Что бы я хотел

- это правило junit4, которое обрабатывает регистрацию исключений для меня.Код

@Rule
public TestRule logException = new TestWatcher() {
    @Override
    public void failed(Description d) {
        catch (Exception e) {
            logger.error("Test ({}) failed because of exception {}", d, e);
            throw e;
        }
    }
}

, конечно, не работает, так как я могу ловить только исключения из блока try.Есть ли обходной путь, чтобы каким-то образом добиться этого таким же простым и общим способом?


Кстати, то, что я сейчас делаю

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

Ответы [ 3 ]

13 голосов
/ 21 сентября 2011

Вам нужно расширить TestRule, в частности apply ().Для примера взгляните на org.junit.rules.ExternalResource & org.junit.rules.TevenFolder.

ExternalResource выглядит следующим образом:

public abstract class ExternalResource implements TestRule {
    public Statement apply(Statement base, Description description) {
        return statement(base);
    }

    private Statement statement(final Statement base) {
        return new Statement() {
            @Override
            public void evaluate() throws Throwable {
                before();
                try {
                    base.evaluate();
                } finally {
                    after();
                }
            }
        };
    }

    /**
     * Override to set up your specific external resource.
     * @throws if setup fails (which will disable {@code after}
     */
    protected void before() throws Throwable {
        // do nothing
    }

    /**
     * Override to tear down your specific external resource.
     */
    protected void after() {
        // do nothing
    }
}

TemporaryFolder затем расширяет это и реализуетbefore () и after ().

public class TemporaryFolder extends ExternalResource {
    private File folder;

    @Override
    protected void before() throws Throwable {
        // create the folder
    }

    @Override
    protected void after() {
        // delete the folder
    }

Таким образом, before вызывается перед testMethod, а after вызывается в finally, но вы можете перехватывать и регистрировать любые исключения, например:

    private Statement statement(final Statement base) {
        return new Statement() {
            @Override
            public void evaluate() throws Throwable {
                before();
                try {
                    base.evaluate();
                } catch (Exception e) {
                    log.error("caught Exception", e);
                } finally {
                    after();
                }
            }
        };
    }

РЕДАКТИРОВАТЬ: работает следующее:

public class SoTest {
    public class ExceptionLoggingRule implements TestRule {
        public Statement apply(Statement base, Description description) {
            return statement(base);
        }

        private Statement statement(final Statement base) {
            return new Statement() {
                @Override
                public void evaluate() throws Throwable {
                    try {
                        base.evaluate();
                    } catch (Exception e) {
                        System.out.println("caught an exception");
                        e.printStackTrace(System.out);
                        throw e;
                    }
                }
            };
        }
    }

    @Rule public ExceptionLoggingRule exceptionLoggingRule = new ExceptionLoggingRule();
    @Rule public ExpectedException expectedException = ExpectedException.none();

    @Test
    public void testMe() throws Exception {
        expectedException.expect(IOException.class);
        throw new IOException("here we are");
    }
}

Тест проходит, и вы получаете следующий вывод:

caught an exception
java.io.IOException: here we are
    at uk.co.farwell.junit.SoTest.testMe(SoTest.java:40)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
...

Порядок применения правил - ExpectedException, который вызываетExceptionLoggingRule, который вызывает метод testMe.ExceptionLoggingRule перехватывает исключение, регистрирует его и перебрасывает, а затем обрабатывает ExpectedException.

Если вы хотите регистрировать только непредвиденные исключения, вы просто переключаете порядок объявления правил:

    @Rule public ExpectedException expectedException = ExpectedException.none();
    @Rule public ExceptionLoggingRule exceptionLoggingRule = new ExceptionLoggingRule();

Таким образом, Ожидаемое исключение применяется первым (то есть вложенным в exceptionLoggingRule) и только сбрасывает исключения, которые не ожидаются.Кроме того, если какое-то исключение было ожидаемым и ни одного не произошло, ожидается, что ExException выдаст ошибку AssertionError, которая также будет зарегистрирована.

Этот порядок оценки не гарантирован, но он вряд ли изменится, если вы не играете с очень разными JVM или не наследуете между классами Test.

Если порядок оценки важен,тогда вы всегда можете передать одно правило другому для оценки.

РЕДАКТИРОВАТЬ: С недавно выпущенным Junit 4.10 вы можете использовать @RuleChain для правильной цепочки правил:

public static class UseRuleChain {
   @Rule
   public TestRule chain= RuleChain
                          .outerRule(new LoggingRule("outer rule")
                          .around(new LoggingRule("middle rule")
                          .around(new LoggingRule("inner rule");

   @Test
   public void example() {
           assertTrue(true);
   }
}

записывает журнал

starting outer rule
starting middle rule
starting inner rule
finished inner rule
finished middle rule
finished outer rule
2 голосов
/ 21 сентября 2011

Посмотрите за рамки ... Что вы используете для запуска тестов?Большинство сред для выполнения тестов (например, Ant, Jenkins, Maven и т. Д.) Имеют средства для использования testrunner, который выводит файл XML, с поддержкой агрегирования файлов XML из набора в всеобъемлющий отчет.

0 голосов
/ 06 октября 2011

Это кажется настолько простым, что я думаю, что я ошибаюсь, а вы спрашиваете что-то другое, но, возможно, я могу помочь:

JUnit 4.X

@ Test (ожидается = Exception.class)

пройдет тест, если в тесте возникнет исключение, или произойдет сбой с сообщением, захваченным инфраструктурой Junit

...