spring 对依赖于请求上下文的方法进行单元测试

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9419606/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 04:57:59  来源:igfitidea点击:

Unit testing a method dependent to the request context

springspring-mvc

提问by satoshi

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.

java.lang.IllegalStateException: No thread-bound request found: 您是指实际 Web 请求之外的请求属性,还是在原始接收线程之外处理请求?如果您实际上是在 Web 请求中操作并且仍然收到此消息,则您的代码可能在 DispatcherServlet/DispatcherPortlet 之外运行:在这种情况下,请使用 RequestContextListener 或 RequestContextFilter 来公开当前请求。

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.

非常感谢。

回答by pap

Spring-test has a flexible request mock called MockHttpServletRequest.

Spring-test 有一个灵活的请求模拟,称为 MockHttpServletRequest。

MockHttpServletRequest request = new MockHttpServletRequest();
RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request));

回答by nicholas.hauschild

You can mock/stub the RequestAttributesobject to return what you want, and then call RequestContextHolder.setRequestAttributes(RequestAttributes)with your mock/stub before you start your test.

您可以模拟/存根RequestAttributes对象以返回您想要的内容,然后RequestContextHolder.setRequestAttributes(RequestAttributes)在开始测试之前使用模拟/存根调用。

@Mock
private RequestAttributes attrs;

@Before
public void before() {
    MockitoAnnotations.initMocks(this);
    RequestContextHolder.setRequestAttributes(attrs);

    // do you when's on attrs
}

@Test
public void testIt() {
    // do your test...
}

回答by maazadeeb

I was able to do the same as this answer, but without the MockHttpServletRequestclass using the @Mockannotation. I guess they're similar. Just posting here for future visitors.

我能够做与这个答案相同的事情,但没有MockHttpServletRequest使用@Mock注释的班级。我猜他们很相似。只是张贴在这里为未来的访客。

@Mock
HttpServletRequest request;

@Before
public void setup() {
    MockitoAnnotations.initMocks(this);
    RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request));
}

回答by Paul Grime

Assuming your class is something like:

假设你的班级是这样的:

class ClassToTest {
    public void doSomething() {
        String sessionId = RequestContextHolder.currentRequestAttributes().getSessionId();
        // Do something with sessionId
    }
}

If you don't have the ability to change the class that uses RequestContextHolder, then you could override the RequestContextHolderclass 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.

如果您无法更改使用 的类RequestContextHolder,则可以RequestContextHolder在测试代​​码中覆盖该类。即您在同一个包中创建一个同名的类,并确保它在实际的 Spring 类之前加载。

package org.springframework.web.context.request;

public class RequestContextHolder {
    static RequestAttributes currentRequestAttributes() {
        return new MyRequestAttributes();
    }

    static class MyRequestAttributes implements RequestAttributes {
        public String getSessionId() {
            return "stub session id";
        }
        // Stub out the other methods.
    }
}

Now, when your tests run, they will pick up your RequestContextHolderclass 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.

现在,当你的测试运行时,他们会选择你的RequestContextHolder类并使用它而不是 Spring 类(假设类路径是为发生这种情况而设置的)。这不是让您的测试运行的特别好方法,但如果您无法更改正在测试的类,则可能有必要。

Alternatively, you could hide the session id retrieval behind an abstraction. For example introduce an interface:

或者,您可以将会话 ID 检索隐藏在抽象之后。例如引入一个接口:

public interface SessionIdAccessor {
    public String getSessionId();
}

Create an implementation:

创建一个实现:

public class RequestContextHolderSessionIdAccessor implements SessionIdAccessor {
    public String getSessionId() {
        return RequestContextHolder.currentRequestAttributes().getSessionId();
    }
}

And use the abstraction in your class:

并在您的课程中使用抽象:

class ClassToTest {
    SessionIdAccessor sessionIdAccessor;

    public ClassToTest(SessionIdAccessor sessionIdAccessor) {
        this.sessionIdAccessor = sessionIdAccessor;
    }

    public void doSomething() {
        String sessionId = sessionIdAccessor.getSessionId();
        // Do something with sessionId
    }
}

Then you can provide a dummy implementation for your tests:

然后你可以为你的测试提供一个虚拟的实现:

public class DummySessionIdAccessor implements SessionIdAccessor {
    public String getSessionId() {
        return "dummy session id";
    }
}

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.

这类事情突出了通常的最佳实践,将某些环境细节隐藏在抽象之后,以便在环境发生变化时可以将它们换掉。这同样适用于通过将虚拟实现替换为“真实”实现来使您的测试不那么脆弱。

回答by Ralph

If the method containing:

如果方法包含:

String sessionId = RequestContextHolder.currentRequestAttributes().getSessionId();

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.

是 web 控制器方法,那么我建议更改方法签名,以便您/spring 将请求作为单独的参数传递给方法。

Then you can remove the troublemaker part String RequestContextHolder.currentRequestAttributes()and use the HttpSessiondirecly.

然后您可以删除麻烦制造者部分字符串RequestContextHolder.currentRequestAttributes()并直接使用HttpSession

Then it should be very easy to use a mocked Session (MockHttpSession) object in the test.

那么MockHttpSession在测试中使用一个mocked Session( ) 对象应该是很容易的。

@RequestMapping...
public ModelAndView(... HttpSession session) {
    String id = session.getId();
    ...
}