Ich schreibe einen Komponententest für eine Methode, die die folgende Zeile enthält:
String sessionId = RequestContextHolder.currentRequestAttributes().getSessionId();
Ich erhalte folgende Fehlermeldung:
java.lang.IllegalStateException: Keine threadgebundene Anforderung gefunden: Verweisen Sie auf Anforderungsattribute außerhalb einer tatsächlichen Webanforderung oder verarbeiten Sie eine Anforderung außerhalb des ursprünglich empfangenden Threads? Wenn Sie tatsächlich innerhalb einer Webanforderung arbeiten und diese Nachricht weiterhin erhalten, wird Ihr Code wahrscheinlich außerhalb von DispatcherServlet / DispatcherPortlet ausgeführt: Verwenden Sie in diesem Fall RequestContextListener oder RequestContextFilter, um die aktuelle Anforderung verfügbar zu machen.
Der Grund liegt auf der Hand - ich führe den Test nicht in einem Anforderungskontext aus.
Die Frage ist, wie ich eine Methode testen kann, die einen Aufruf einer vom Anforderungskontext abhängigen Methode in einer Testumgebung enthält.
Vielen Dank.
quelle
Sie können das
RequestAttributes
Objekt verspotten / stubben, um das zurückzugeben, was Sie möchten, und dannRequestContextHolder.setRequestAttributes(RequestAttributes)
mit Ihrem Mock / stub aufrufen, bevor Sie mit dem Test beginnen.@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... }
quelle
Ich konnte das Gleiche wie diese Antwort tun , jedoch ohne die
MockHttpServletRequest
Klasse, die die@Mock
Anmerkung verwendet. Ich denke, sie sind ähnlich. Einfach hier für zukünftige Besucher posten.@Mock HttpServletRequest request; @Before public void setup() { MockitoAnnotations.initMocks(this); RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request)); }
quelle
Angenommen, Ihre Klasse ist so etwas wie:
class ClassToTest { public void doSomething() { String sessionId = RequestContextHolder.currentRequestAttributes().getSessionId(); // Do something with sessionId } }
Wenn Sie nicht in der Lage sind, die verwendete Klasse zu ändern
RequestContextHolder
, können Sie dieRequestContextHolder
Klasse in Ihrem Testcode überschreiben . Das heißt, Sie erstellen eine Klasse mit demselben Namen im selben Paket und stellen sicher, dass sie vor der eigentlichen Spring-Klasse geladen wird.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. } }
Wenn Ihre Tests ausgeführt werden, nehmen sie Ihre
RequestContextHolder
Klasse auf und verwenden diese vor der Spring-Klasse (vorausgesetzt, der Klassenpfad ist dafür eingerichtet). Dies ist keine besonders gute Möglichkeit, Ihre Tests zum Laufen zu bringen, aber es kann erforderlich sein, wenn Sie die zu testende Klasse nicht ändern können.Alternativ können Sie das Abrufen der Sitzungs-ID hinter einer Abstraktion verbergen. Stellen Sie zum Beispiel eine Schnittstelle vor:
public interface SessionIdAccessor { public String getSessionId(); }
Erstellen Sie eine Implementierung:
public class RequestContextHolderSessionIdAccessor implements SessionIdAccessor { public String getSessionId() { return RequestContextHolder.currentRequestAttributes().getSessionId(); } }
Und verwenden Sie die Abstraktion in Ihrer Klasse:
class ClassToTest { SessionIdAccessor sessionIdAccessor; public ClassToTest(SessionIdAccessor sessionIdAccessor) { this.sessionIdAccessor = sessionIdAccessor; } public void doSomething() { String sessionId = sessionIdAccessor.getSessionId(); // Do something with sessionId } }
Dann können Sie eine Dummy-Implementierung für Ihre Tests bereitstellen:
public class DummySessionIdAccessor implements SessionIdAccessor { public String getSessionId() { return "dummy session id"; } }
Diese Art von Dingen hebt eine übliche Best Practice hervor, um bestimmte Umgebungsdetails hinter Abstraktionen zu verbergen, damit Sie sie austauschen können, wenn sich Ihre Umgebung ändert. Dies gilt auch, um Ihre Tests weniger spröde zu machen, indem Sie Dummy-Implementierungen gegen "echte" austauschen.
quelle
Wenn die Methode enthält:
String sessionId = RequestContextHolder.currentRequestAttributes().getSessionId();
Ist die Web-Controller-Methode, dann würde ich empfehlen, die Methodensignatur so zu ändern, dass Sie / spring die Anforderung als separaten Paremter an die Methode übergeben.
Dann können Sie den Unruhestifter-Teil String entfernen
RequestContextHolder.currentRequestAttributes()
und denHttpSession
direkt verwenden.Dann sollte es sehr einfach sein, ein verspottetes Session (
MockHttpSession
) -Objekt im Test zu verwenden.@RequestMapping... public ModelAndView(... HttpSession session) { String id = session.getId(); ... }
quelle