假设您的课程是这样的:
class ClassToTest {
public void doSomething() {
String sessionId = RequestContextHolder.currentRequestAttributes().getSessionId();
}
}
如果您没有能力更改使用的类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";
}
}
}
现在,当您运行测试时,他们将接管您的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();
}
}
然后,您可以为测试提供虚拟实现:
public class DummySessionIdAccessor implements SessionIdAccessor {
public String getSessionId() {
return "dummy session id";
}
}
这种事情强调了通常的最佳做法,即将某些环境细节隐藏在抽象后面,以便在环境变化时可以将其替换掉。通过将虚拟实现替换为“真实”实现,这同样适用于使您的测试不那么脆弱。