Blog

Mocking Static Calls Revisited

Jeroen van Erp

Aktualisiert November 11, 2025
2 Minuten

Gestern habe ich Ihnen ein Problem geschildert, das wir mit dem Mocking des statischen Aufrufs von FacesContext.getCurrentInstance() hatten. Die drei von mir vorgestellten Lösungen fühlten sich alle irgendwie falsch an. In den Kommentaren wurde eine vierte Möglichkeit genannt: AOP. Heute werde ich Ihnen eine weitere Lösung vorstellen, die sich in jeder Hinsicht richtig anfühlt.

Irgendwie scheint alles einfacher zu sein, wenn Sie einen Blick auf den Code werfen. Ich wusste bereits, dass FacesContext eine ThreadLocal-Variable ist. Aber erst heute, als ich den Code sah, wurde mir klar, was ich übersehen hatte. Im Grunde sieht die abstrakte Basisklasse javax.faces.context.FacesContext wie folgt aus:

public abstract class FacesContext {
  private static ThreadLocal _currentInstance = new ThreadLocal();
  // Viele abstrakte Methoden.
  public static FacesContext getCurrentInstance() {
  return (FacesContext)_currentInstance.get();
  }
  protected static void setCurrentInstance(FacesContext context) {
  _currentInstance.set(context);
  }
}

Das bedeutet, dass wir, wenn wir unsere eigene Version eines FacesContextes implementieren, diesen selbst im ThreadLocal einstellen können! Die Lösung liegt in einer MockFacesContextWrapper-Klasse, die wie folgt implementiert wird:

public class MockFacesContextWrapper extends FacesContext {
  private FacesContext mockContext;
  public MockFacesContextWrapper(FacesContext context) {
  this.mockContext = context;
  FacesContext.setCurrentInstance(this);
  }
  // Delegieren Sie Methoden für mockContext für alle deklarierten abstrakten Methoden in FacesContext
}

Jetzt können Ihre Tests mit einem Mocked FacesContext implementiert werden, ohne dass Ihr Quellcode weiß, dass er getestet wird. Ein Beispiel für einen Testfall:

public class SomeFacesBeanTest extends TestCase {
  private FacesContext facesContext;
  public void setUp() {
  facesContext = EasyMock.createMock(FacesContext.class);
  new MockFacesContextWrapper(facesContext);
  }
  // Ihre Testmethoden.
}

Mir gefällt, dass der zu testende Code jetzt nicht mehr angepasst werden muss. Sie können so programmieren, wie Sie es gewohnt sind, ohne eine Umleitungsmethode zu schreiben, um den statischen Aufruf darin zu verpacken, oder ein neues Feld in jeder Klasse einzuführen, die den FacesContext verwendet. Wie bewerten Sie diese Lösung im Vergleich zu den gestern im Blog und in den Kommentaren vorgestellten Lösungen?

Verfasst von

Jeroen van Erp

Contact

Let’s discuss how we can support your journey.