对依赖于请求上下文的方法进行单元测试
我正在为包含以下行的方法编写单元测试:
String sessionId = RequestContextHolder.currentRequestAttributes().getSessionId();
我收到以下错误:
java.lang.IllegalStateException:未找到线程绑定请求:是 您指的是实际 Web 请求之外的请求属性, 或者在最初接收线程之外处理请求?如果 您实际上是在网络请求中进行操作并且仍然收到此消息 消息,您的代码可能在外部运行 DispatcherServlet/DispatcherPortlet:在本例中,使用 RequestContextListener 或 RequestContextFilter 暴露当前 请求。
原因很明显——我没有在请求上下文中运行测试。
问题是,如何在测试环境中测试包含对依赖于请求上下文的方法的调用的方法?
非常感谢。
I'm writing a unit test for a method that contains the following line:
String sessionId = RequestContextHolder.currentRequestAttributes().getSessionId();
I get the following error:
java.lang.IllegalStateException: No thread-bound request found: Are
you referring to request attributes outside of an actual web request,
or processing a request outside of the originally receiving thread? If
you are actually operating within a web request and still receive this
message, your code is probably running outside of
DispatcherServlet/DispatcherPortlet: In this case, use
RequestContextListener or RequestContextFilter to expose the current
request.
The reason is quite obvious — I'm not running the test in a request context.
The question is, how can I test a method that contains a call to a method dependent to the request context in a test environnment?
Thank you very much.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
假设您的类类似于:
如果您无法更改使用
RequestContextHolder
的类,那么您可以在测试代码中重写RequestContextHolder
类。即,您在同一个包中创建一个具有相同名称的类,并确保它在实际的 Spring 类之前加载。
现在,当您的测试运行时,它们将选取您的
RequestContextHolder
类并优先使用该类(而不是 Spring 类)(假设已为此情况设置了类路径)。这不是运行测试的特别好的方法,但如果您无法更改正在测试的类,则可能有必要。
或者,您可以将会话 ID 检索隐藏在抽象后面。例如,引入一个接口:
创建一个实现:
并在您的类中使用抽象:
然后您可以为您的测试提供一个虚拟实现:
这种事情突出了将某些环境细节隐藏在抽象后面的通常最佳实践,以便您可以如果您的环境发生变化,请更换它们。
这同样适用于通过将虚拟实现替换为“真实”实现来降低测试的脆弱性。
Assuming your class is something like:
If you don't have the ability to change the class that uses
RequestContextHolder
, then you could override theRequestContextHolder
class in your test code.I.e. you create a class with the same name, in the same package, and ensure it is loaded before the actual Spring class.
Now, when your tests run, they will pick up your
RequestContextHolder
class and use that in preference to the Spring one (assuming the classpath is set up for this to happen).This isn't a particular nice way of getting your tests to run, but it might be necessary if you can't change the class you are testing.
Alternatively, you could hide the session id retrieval behind an abstraction. For example introduce an interface:
Create an implementation:
And use the abstraction in your class:
Then you can provide a dummy implementation for your tests:
This sort of thing highlights a usual best-practice to hide certain environmental details behind abstractions so that you can swap them out if your environment changes.
This applies equally to making your tests less brittle by swapping dummy implementations for 'real' ones.
如果方法包含:
是Web控制器方法,那么我建议更改方法签名,以便您/spring将请求作为单独的参数传递给该方法。
然后你可以删除麻烦部分 String
RequestContextHolder.currentRequestAttributes()
并直接使用HttpSession
。那么在测试中使用模拟的 Session (
MockHttpSession
) 对象应该很容易。If the method containing:
is the web controller method, then I would recommend to change the method signature, so that you/spring pass the Request as an seperate paremter to the method.
Then you can remove the troublemaker part String
RequestContextHolder.currentRequestAttributes()
and use theHttpSession
direcly.Then it should be very easy to use a mocked Session (
MockHttpSession
) object in the test.Spring-test 有一个灵活的请求模拟,称为 MockHttpServletRequest。
Spring-test has a flexible request mock called MockHttpServletRequest.
您可以模拟/存根
RequestAttributes
对象返回你想要的,然后调用RequestContextHolder.setRequestAttributes(RequestAttributes)
在开始测试之前使用您的模拟/存根。You can mock/stub the
RequestAttributes
object to return what you want, and then callRequestContextHolder.setRequestAttributes(RequestAttributes)
with your mock/stub before you start your test.我能够执行与此 answer 相同的操作,但没有使用
MockHttpServletRequest
类code>@Mock 注解。我猜他们很相似。只是在这里发布以供未来的访客使用。I was able to do the same as this answer, but without the
MockHttpServletRequest
class using the@Mock
annotation. I guess they're similar. Just posting here for future visitors.