使用 Spring AOP 和 @Transactional 时如何编写有用的单元/集成测试?
我有一个围绕特定服务定义的日志记录方面。我正在使用 Spring AOP 并建议许多服务方法,以便记录与这些方法调用相关的特定应用程序事件。
例如,我使用 @AfterThrowing 注释来检测方法调用中的失败,以便我可以相应地记录。我的服务方法被标记为@Transactional。
正如您可能知道的那样,在提交事务后调用日志记录方面逻辑非常重要,否则我的日志记录方面将错过与提交失败的事务相关的任何错误。更糟糕的是,即使在事务提交后该方法实际上失败了,与成功方法调用相关的任何日志消息都会被写入。
通过确保正确定义方面顺序优先级,我可以完美地完成此操作。我最大的问题是我希望能够编写一个测试(集成测试似乎是唯一的选择),该测试将明确确认顺序优先级得到遵守。鉴于顺序优先级是一种配置,很容易预见到将来有人会调整配置,而没有意识到他们正在破坏关键代码。
因此,我的理论是,我需要编写一个测试,故意导致事务在提交时失败,然后检查日志记录 @AfterThrowing 切入点中的逻辑是否随后被调用。
以前有人遇到过这种需求吗?我确信这是使用 AOP 时的常见场景。
I have a logging aspect defined around a particular service. I am using Spring AOP and advising a number of service methods in order to log specific application events related to those method calls.
For example, I am using the @AfterThrowing annotation to detect a failures in method calls so I can log accordingly. My service methods are marked @Transactional.
As you can probably tell, it is extremely important that the logging aspect logic is invoked after the transaction is committed, otherwise my logging aspect would miss any errors associated with transactions failing on commit. Worse still, any log messages associated with successful method invocation would be written even though the method actually failed once the transaction was committed.
I have this working perfectly by ensuring I define the aspect order precedence correctly. My biggest issue is that I want to be able to write a test (integration test seems the only option) that will categorically confirm that the order precedence is being honoured. Given that the order precedence is a configuration, it is easy to foresee someone coming along in the future and tweaking configuration without realising they are breaking critical code.
So, my theory is that I need to write a test that deliberately causes a transaction to fail on commit and then checks that the logic in my logging @AfterThrowing pointcuts are called afterwards.
Anyone ever come across this need before? I'm sure it is a common scenario when using AOP.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
如果您编写一个上下文感知的测试,您可以从上下文中获取所有方面,并断言顺序是预期的。
另一个解决方案是找到任何可能引发重复键异常的资源,然后使用相同的值调用它两次。至少第二次调用应该记录异常。
If you write a test that is context aware you could get all aspects from the context and assert that the order is the intended.
Another solution is to find any resource that could throw a duplicate key exception and just call it twice with the same value. At least the second call should log the exception.
您可能想要加载整个 Spring 上下文来进行测试,除了数据源。有关数据库的内容,请查看 MockRunner。它有一组很好的模拟 JDBC 类。
在 Spring 上下文中(可能通过单独的 xml 文件),将真实的 DataSource 替换为
MockDataSource
进行测试。从测试用例的上下文中检索此 DataSource,您可以执行以下操作:请注意,如果您想引发异常,结果集处理程序(通过
AbstractResultSetHandler
)有一个prepareThrowsSQLException ()
,您可以使用它来指定特定的 SQL 字符串应引发异常。根据您在每个测试用例之前需要设置的内容,将上述内容与
@Before
和@BeforeClass
方法混合并匹配。请注意,模拟 JDBC 语句保留所有执行的 SQL 的记录,因此如果您运行大量 JDBC 调用(数百万?),内存/性能可能会成为问题。为每个测试用例创建一个新的连接/语句可能是最简单的。最后,如果您使用 Maven,MockRunner 会引入许多 JDBC 模拟不需要的其他东西。以下是我在 pom.xml 中定义它的方式:
You probably want to load your entire Spring context for the tests except for the DataSource. For the database stuff, take a look at MockRunner. It has a good set of mock JDBC classes.
In your Spring context (maybe via a separate xml file), swap out your real DataSource with a
MockDataSource
for the test. Retrieve this DataSource from the context in your test cases and you can do something like the following:Note that if you want to throw an exception, the result set handler (via
AbstractResultSetHandler
) has aprepareThrowsSQLException()
which you can use to specify that a specific SQL string should throw an exception.Mix and match the above with
@Before
and@BeforeClass
methods depending on what you need to setup before each test case. Be aware that Mock JDBC Statements keep a record of all executed SQL, so if you are running a huge number of JDBC calls (millions?), memory / performance could be an issue. It may be easiest to just create a new Connection / Statement for each test case.Finally, if you are using Maven, MockRunner pulls in a lot of other things you don't need for JDBC mocks. Here's how I have it defined in the pom.xml: