假设你的班级是这样的:
class ClassToTest {
public void doSomething() {
String sessionId = RequestContextHolder.currentRequestAttributes().getSessionId();
// Do something with sessionId
}
}
如果您无法更改使用RequestContextHolder 的类,那么您可以在测试代码中覆盖RequestContextHolder 类。
IE。你在同一个包中创建一个同名的类,并确保它在实际的 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.
}
}
现在,当您的测试运行时,它们会选择您的 RequestContextHolder 类并优先使用该类而不是 Spring 类(假设为发生这种情况设置了类路径)。
这不是让您的测试运行的特别好的方法,但如果您无法更改您正在测试的类,则可能需要这样做。
或者,您可以将会话 ID 检索隐藏在抽象后面。比如引入一个接口:
public interface SessionIdAccessor {
public String getSessionId();
}
创建一个实现:
public class RequestContextHolderSessionIdAccessor implements SessionIdAccessor {
public String getSessionId() {
return RequestContextHolder.currentRequestAttributes().getSessionId();
}
}
并在你的类中使用抽象:
class ClassToTest {
SessionIdAccessor sessionIdAccessor;
public ClassToTest(SessionIdAccessor sessionIdAccessor) {
this.sessionIdAccessor = sessionIdAccessor;
}
public void doSomething() {
String sessionId = sessionIdAccessor.getSessionId();
// Do something with sessionId
}
}
然后您可以为您的测试提供一个虚拟实现:
public class DummySessionIdAccessor implements SessionIdAccessor {
public String getSessionId() {
return "dummy session id";
}
}
这类事情突出了一种通常的最佳实践,即在抽象背后隐藏某些环境细节,以便在环境发生变化时将它们换掉。
这同样适用于通过将虚拟实现替换为“真实”实现来降低测试的脆弱性。