如何在 JUnit 中构建自动回复 JMS 监听器(在 OpenEJB 中)
我有一个 EJB 来向 JMS 队列发送消息并等待它的回复。我想测试EJB,使用OpenEJB进行EJB的JUnit测试很容易。但问题是这个 EJB 将等待 JMS 响应才能继续处理。
虽然我可以在 junit 代码中发送消息,但由于 EJB 仍在进行中,因此在 EJB 完成之前我无法运行它。
第二个解决方案是我可以初始化一个 MDB 来侦听并回复来自 EJB 的 JMS 消息,但问题是 MDB 必须位于 src\main\java 中,而不能位于 src\test\java 中。问题是这只是一个测试代码,我不应该将其打包到生产环境中。 (我使用 Maven)
或者我应该使用模拟对象?
I have a EJB to send a message to JMS queue and wait the reply from it. I want to test the EJB, it's easy to use OpenEJB to do the JUnit test of the EJB. But the problem is this EJB will wait the JMS response to continue process.
Although I can send message in my junit code, but because the EJB is still on-going, I cannot run it before the EJB is completed.
2nd solution is I can initialize a MDB to listen and reply the JMS message form the EJB, but the problem is the MDB must in src\main\java and cannot in src\test\java. The problem is this is just a test code and I should not package it to production environment. (I use Maven)
Or should I use mock object ?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
你走在正确的轨道上。有几种方法可以处理这个问题。以下是使用 OpenEJB 和 Maven 进行单元测试的一些技巧。
测试 bean
您可以编写各种 EJB 和其他测试实用程序并部署它们。您所需要的只是一个用于测试代码的
ejb-jar.xml
,如下所示:src/main/resources/ejb-jar.xml
(正常的)src/test/resources/ejb-jar.xml
(测试 bean)像往常一样,仅此而已。它的存在只是告诉 OpenEJB 检查类路径的该部分并扫描其中的 Bean。扫描整个类路径非常慢,因此这只是加快速度的约定。
ejb-jar.xml
文件只需要包含TestCase 注入
通过上面的 src/test/resources/ejb-jar.xml,您可以非常轻松地添加仅测试 MDB,并将其设置为以 TestCase 需要的方式处理请求。但是 src/test/resources/ejb-jar.xml 还提供了一些其他有趣的功能。
您可以让 TestCase 本身通过声明对您需要的任何 JMS 资源的引用并将它们注入来完成此操作。
现在您只需一个线程就可以响应测试用例本身的 JMS 消息。您可以启动一个小的可运行程序,它将读取一条消息,发送您想要的响应,然后退出。
也许类似于:
查看
备用描述符
如果您确实想使用 MDB 解决方案并且只想为一个测试而不是所有测试启用它,您可以在特殊的 src/test/resources/mockmdb 中定义它。 ejb-jar.xml 文件并在需要它的特定测试用例中启用它。
有关如何启用该描述符以及备用描述符的各种选项的更多信息,请参阅此文档。
You're on the right track. There area few ways to handle this. Here are a couple tips for unit testing with OpenEJB and Maven.
Test beans
You can write all sorts of EJBs and other testing utilities and have them deployed. All you need is a
ejb-jar.xml
for the test code like so:src/main/resources/ejb-jar.xml
(the normal one)src/test/resources/ejb-jar.xml
(the testing beans)As usual the
ejb-jar.xml
file only needs to contain<ejb-jar/>
and nothing more. Its existence simply tells OpenEJB to inspect that part of the classpath and scan it for beans. Scanning the entire classpath is very slow, so this is just convention to speed that up.TestCase injection
With the above
src/test/resources/ejb-jar.xml
you could very easily add that test-only MDB and have it setup to process the request in a way that the TestCase needs. But thesrc/test/resources/ejb-jar.xml
also opens up some other interesting functionality.You could have the
TestCase
itself do it by declaring references to whatever JMS resources you need and have them injected.Now you're just one thread away from being able to respond to the JMS message the testcase itself. You can launch off a little runnable that will read a single message, send the response you want, then exit.
Maybe something like:
See
Alternate Descriptors
If you did want to use the MDB solution and only wanted to enable it for just the one test and not all tests, you could define it in a special
src/test/resources/mockmdb.ejb-jar.xml
file and enable it in the specific test case(s) where it is needed.See this doc for more information on how to enable that descriptor and the various options of alternate descriptors.
我认为你应该为此使用模拟。如果您正在向真实的 JMS 服务器发送消息、侦听消息、回复消息等,那么您所做的不是单元测试。我不打算讨论应该叫什么,但我认为单元测试不应该与实时数据库、消息队列等进行交互,这一点已经被普遍接受。
I think you should use mocks for this. If you're sending messages to a real JMS server, listening for them, replying to them, etc. then you're doing something other than a unit test. I'm not going to get into the argument about what that should be called, but I think it's pretty well universally accepted that a unit-test shouldn't be talking to live databases, message queues, etc.
如果我正确理解你的问题 - 让 EJB 发送 JMS 消息然后等待响应是一个糟糕的设计,实际上与 EJB 的整个思想相矛盾。
您发送了一条 JMS 消息,然后就忘记了它。您有一个 MDB 来接收消息。如果 EJB 依赖于响应,那么 JMS 不是最佳选择,而是使用另一个 EJB。
要测试发送,请模拟 JMS 类,并单独测试 MDB。
EJB 是为同步任务而设计的,JMS 是为异步任务而设计的 - 如果您必须与外部系统进行异步通信,我建议您在此之后设计您的系统,并执行适当的异步流程。等待 JMS 回复的 EJB 充其量只是一种丑陋的黑客行为,并且不会为您的系统设计带来任何好处。
If I've understood your question correct - It's a bad design to have an EJB send a JMS message and then await a response, in fact contradictory to the whole idea of EJB.
You send a JMS message, and then forget about it. You have an MDB to receive the message. If the EJB depends on a response, JMS is not the way to go, but rather use another EJB.
To test the sending, mock the JMS classes, test the MDB separately.
EJB's are designed for synchronous tasks, JMS for asynchronous tasks - if you have to do asynchronous communication to an external system, I suggest you design your system after that, and do proper asynchronous flows. An EJB that sits and waits for a JMS reply is at best an ugly hack, and will not add any good to your system design.
感谢大卫的回答,这就是我想要的。我知道单元测试不应该依赖于其他外部资源,例如 JMS 服务器。但如果我使用Maven + OpenEJB,我仍然可以让测试代码在封闭的环境中进行。它可以帮助自动测试外部资源依赖,特别是对于一些不易重构的旧程序。
如果您在initialContext.bind("inject", this) 中看到以下错误消息,
请确保该类使用@org.apache.openejb.api.LocalClient 进行注释,并且已成功发现和部署。
一种参考是 http://openejb.apache.org/3.0/local-client -injection.html,但添加“openejb.tempclassloader.skip=annotations”对我不起作用。请检查此文档OpenEJB 本地客户端注入失败。已经有一个补丁了,我认为它将在 OpenEJB 3.1.5 或 4.0 中修复
Thanks for David's answer, it's what I want. I know unit test should not depend on other external resource like JMS server. But if I use Maven + OpenEJB, I still can let the test code in a closed environment. It can help to do automatically test with external resource dependency, especially for some old programs which not easy to refactor.
And if you see the following error message in initialContext.bind("inject", this)
Ensure that class was annotated with @org.apache.openejb.api.LocalClient and was successfully discovered and deployed.
One reference is http://openejb.apache.org/3.0/local-client-injection.html, but add "openejb.tempclassloader.skip=annotations" doesn't work for me. Please check this doc OpenEJB Local Client Injection Fails. There is already a patch for it, I think it will be fixed in OpenEJB 3.1.5 or 4.0
我还发现最好的做法是将 MDB 中的逻辑实际分解到不同的类。这将您的业务逻辑与 MDB 隔离开来,并允许您以多种方式公开您的逻辑(MDB、EJB、Web 服务、POJO 等)。它还允许您更轻松地测试业务逻辑,而无需测试协议(在本例中为 JMS)。
至于测试 JMS,模拟可能是更好的选择。或者,如果您确实需要“在容器中”测试协议,请考虑使用 JBoss Microcontainer 之类的东西(我相信您可以将其与 Seam 等 JBoss 项目一起打包)。然后您可以启动一个迷你容器来测试 EJB 和 JMS 等内容。
但总的来说,除非绝对必要,否则最好避免使用容器。这就是为什么将业务逻辑与实现逻辑分开(即使您不使用模拟)是一个很好的实践。
Also I've found it is best practice to actually break out your logic in your MDB to a different class. This isolates your business logic from being in an MDB and allows you to expose your logic as more than one way (MDB, EJB, Web Service, POJO, etc.). It also allows you to more easily test your business logic without the need to test the protocol (JMS in this case).
As for testing JMS, mocking may be the better choice. Or if you really need to test the protocol "in container" look at using something like the JBoss Microcontainer (I believe you can get this packaged with some of the JBoss projects like Seam). Then you can fire up a mini-container for testing things like EJB and JMS.
But overall, it is best to avoid having to need a container unless absolutely necessary. That's why separating your business logic from your implementation logic (even if you don't use mocks) is a good practice.