使用Spring AOP和@Transactional时,如何编写有用的单元/集成测试?

时间:2011-10-30 17:39:28

标签: spring spring-aop spring-transactions

我有一个围绕特定服务定义的日志记录方面。我正在使用Spring AOP并建议许多服务方法,以便记录与这些方法调用相关的特定应用程序事件。

例如,我使用@AfterThrowing注释来检测方法调用中的失败,以便我可以相应地进行日志记录。我的服务方法标记为@Transactional。

正如您可能知道的那样,在提交事务之后调用日志记录方面逻辑非常重要,否则我的日志记录方面将错过与提交失败的事务相关的任何错误。更糟糕的是,即使事务提交后方法实际上失败,也会写入与成功方法调用关联的任何日志消息。

通过确保正确定义方面顺序优先顺序,我完美地完成了这项工作。我最大的问题是我希望能够编写一个测试(集成测试似乎是唯一的选项),它将明确地确认订单优先级是否得到尊重。鉴于订单优先级是一种配置,很容易预见将来有人会调整配置而不会意识到他们正在破坏关键代码。

所以,我的理论是我需要编写一个测试,故意导致事务在提交时失败,然后检查我的日志@AfterThrowing切入点中的逻辑是否随后被调用。

以前有人遇到过这种需求吗?我确信这是使用AOP时的常见情况。

2 个答案:

答案 0 :(得分:1)

如果你编写一个具有上下文感知能力的测试,你可以从上下文中获取所有方面,并断言订单是预期的。

另一种解决方案是找到任何可能引发重复键异常的资源,并使用相同的值调用它两次。至少第二次调用应记录异常。

答案 1 :(得分:1)

您可能希望为DataSource的之外的测试加载整个Spring上下文。对于数据库内容,请查看MockRunner。它有一组很好的模拟JDBC类。

在Spring上下文中(可能通过一个单独的xml文件),用MockDataSource替换您的真实DataSource进行测试。从测试用例中的上下文中检索此DataSource,您可以执行以下操作:

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调用(数百万?),内存/性能可能会成为问题。为每个测试用例创建一个新的Connection / Statement可能是最容易的。

最后,如果你使用的是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>