Как написать полезные модульные / интеграционные тесты при использовании Spring AOP и @Transactional? - PullRequest
2 голосов
/ 30 октября 2011

У меня есть аспект ведения журнала, определенный для конкретной службы. Я использую Spring AOP и советую несколько методов обслуживания для регистрации определенных событий приложения, связанных с этими вызовами методов.

Например, я использую аннотацию @AfterThrowing для обнаружения сбоев в вызовах методов, чтобы я мог соответствующим образом регистрироваться. Мои методы обслуживания помечены @ Transactional.

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

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

Итак, моя теория состоит в том, что мне нужно написать тест, который намеренно вызывает сбой транзакции при фиксации, а затем проверяет, что логика в моих журналах @AfterThrowing вызывается впоследствии.

Кто-нибудь когда-нибудь сталкивался с этой необходимостью раньше? Я уверен, что это обычный сценарий при использовании АОП.

Ответы [ 2 ]

1 голос
/ 12 ноября 2011

Возможно, вы хотите загрузить весь контекст Spring для тестов , за исключением для источника данных. Что касается базы данных, взгляните на MockRunner . У него хороший набор фиктивных классов JDBC.

В контексте Spring (возможно, с помощью отдельного XML-файла) замените реальный источник данных на MockDataSource для теста. Извлеките этот источник данных из контекста в ваших тестовых примерах, и вы можете сделать что-то вроде следующего:

MockResultSet rs = new MockResultSet("SELECT 1");
rs.addRow(new Object[] { 1 });

MockConnection con = new MockConnection();
con.getPreparedStatementResultSetHandler().prepareResultSet("SELECT 1", rs);

PreparedStatement ps = connection.prepareStatement("SELECT 1");
ps.executeQuery()

Обратите внимание, что если вы хотите вызвать исключение, обработчик набора результатов (через AbstractResultSetHandler) имеет prepareThrowsSQLException(), который можно использовать, чтобы указать, что конкретная строка SQL должна вызывать исключение.

Смешайте и сопоставьте вышеприведенные методы @Before и @BeforeClass в зависимости от того, что вам нужно настроить перед каждым тестовым набором. Имейте в виду, что фиктивные операторы JDBC хранят записи всех выполненных SQL-запросов, поэтому, если вы выполняете огромное количество вызовов JDBC (миллионы?), Память / производительность могут быть проблемой. Возможно, проще всего создать новое соединение / оператор для каждого теста.

Наконец, если вы используете Maven, MockRunner добавляет много других вещей, которые вам не нужны для макетов JDBC. Вот как я это определил в pom.xml:

<dependency>
    <groupId>com.mockrunner</groupId>
    <artifactId>mockrunner-jdk1.5-j2ee1.3</artifactId>
    <version>0.4</version>
    <scope>test</scope>
    <exclusions>
        <exclusion>
            <groupId>xml-apis</groupId>
            <artifactId>xml-apis</artifactId>
        </exclusion>
        <exclusion>
            <groupId>cglib-nodep</groupId>
            <artifactId>cglib-nodep</artifactId>
        </exclusion>
        <exclusion>
            <groupId>jboss</groupId>
            <artifactId>jboss-jee</artifactId>
        </exclusion>
        <exclusion>
            <groupId>struts</groupId>
            <artifactId>struts</artifactId>
        </exclusion>
        <exclusion>
            <groupId>org.mockejb</groupId>
            <artifactId>mockejb</artifactId>
        </exclusion>
        <exclusion>
            <groupId>nekohtml</groupId>
            <artifactId>nekohtml</artifactId>
        </exclusion>
        <exclusion>
            <groupId>commons-validator</groupId>
            <artifactId>commons-validator</artifactId>
        </exclusion>
        <exclusion>
            <groupId>commons-digester</groupId>
            <artifactId>commons-digester</artifactId>
        </exclusion>
        <exclusion>
            <groupId>commons-beanutils</groupId>
            <artifactId>commons-beanutils</artifactId>
        </exclusion>
        <exclusion>
            <groupId>xerces</groupId>
            <artifactId>xercesImpl</artifactId>
        </exclusion>
        <exclusion>
            <groupId>jdom</groupId>
            <artifactId>jdom</artifactId>
        </exclusion>
        <exclusion>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
        </exclusion>
    </exclusions>
</dependency>
1 голос
/ 12 ноября 2011

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

Другое решение состоит в том, чтобы найти любой ресурс, который может вызвать исключение дублирующегося ключа, и просто вызвать его дважды с тем же значением.По крайней мере, второй вызов должен регистрировать исключение.

...