Servlet-Filter in Java testen: Wie kann man Änderungen im Context vor dem Aufruf von clear() im finally-Block prüfen

DrPils

Bekanntes Mitglied
Hi

Damit in meiner Rest api die timezone konfigurierbar ist, habe ich eine Filter erstellt, der den timezone paramerter entgegen nimmt und diesen dem context übergibt.
Dafür würde ich gerne einen Unit test schreiben. Jedoch setze ich den Context im finally block wieder zurück, was dazu führt, dass ich den Inhalt des contextes nach aufruf der doFilter() nicht mehr überprüfen kann. Die Methode im TimezoneContext kann ich auch nicht spyen, da diese statisch ist.
Kann ich hier irgendwas sinnvolles tun um einen Unit Test zu schreiben?

Java:
@Component
public class TimeZoneFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        String timeZoneParam = httpRequest.getParameter("timezone");

        TimeZoneContext.setTimeZone(ZoneId.of(Objects.requireNonNullElse(timeZoneParam, "UTC")));

        try {
            chain.doFilter(request, response);
        } finally {
            TimeZoneContext.clear();
        }
    }
}

Code:
public class TimeZoneContext {
    private static final ThreadLocal<ZoneId> timeZoneHolder = new ThreadLocal<>();

    public static void setTimeZone(ZoneId zoneId) {
        timeZoneHolder.set(zoneId);
    }

    public static ZoneId getTimeZone() {
        return timeZoneHolder.get() != null ? timeZoneHolder.get() : ZoneId.of("UTC");
    }

    public static void clear() {
        timeZoneHolder.remove();
    }
}

Java:
class TimeZoneContextTest {

    @AfterEach
    void tearDown() {
        TimeZoneContext.clear();
    }

    @Test
    void should_return_default_timezone_utc() {
        ZoneId result = TimeZoneContext.getTimeZone();

        assertEquals(ZoneId.of("UTC"), result);
    }

    @Test
    void should_set_timezone() {
        TimeZoneContext.setTimeZone(ZoneId.of("Europe/Berlin"));

        ZoneId timeZone = TimeZoneContext.getTimeZone();

        assertEquals(ZoneId.of("Europe/Berlin"), timeZone); //TimeZoneContext.clear() wurde im finally block aufgerufen -> context ist leer und returnt "UTC"
    }

}
 
Ich verstehe das Problem nicht ganz - zumindest bezogen auf den Code:
Java:
    @Test
    void should_set_timezone() {
        TimeZoneContext.setTimeZone(ZoneId.of("Europe/Berlin"));

        ZoneId timeZone = TimeZoneContext.getTimeZone();

        assertEquals(ZoneId.of("Europe/Berlin"), timeZone); //TimeZoneContext.clear() wurde im finally block aufgerufen -> context ist leer und returnt "UTC"
    }
Wo wird da ein finally Block aufgerufen? Du testet ja nur die Klasse TimeZoneContext - da gibt es keinen finally Block.
 
Sofern es um den Test der FilterChain geht - ja, da ist das ein Problem. Neben PowerMock (was ich noch nie genutzt habe) wäre eine Lösung einfach die statischen Aufrufe aus der FilterChain rauszuwerfen, und eine TimeZoneContextFassade als Component zu injecten, die nichts anders macht als an den TimeZoneContext durchzureichen. Dann kannst du die Fassade im Test mocken/mit einer Test-Implementierung austauschen
 
Ich verstehe das Problem nicht ganz - zumindest bezogen auf den Code:
Java:
    @Test
    void should_set_timezone() {
        TimeZoneContext.setTimeZone(ZoneId.of("Europe/Berlin"));

        ZoneId timeZone = TimeZoneContext.getTimeZone();

        assertEquals(ZoneId.of("Europe/Berlin"), timeZone); //TimeZoneContext.clear() wurde im finally block aufgerufen -> context ist leer und returnt "UTC"
    }
Wo wird da ein finally Block aufgerufen? Du testet ja nur die Klasse TimeZoneContext - da gibt es keinen finally Block.
Ja vollkommen richtig, habe den falschen Test kopiert
 
Sofern es um den Test der FilterChain geht - ja, da ist das ein Problem. Neben PowerMock (was ich noch nie genutzt habe) wäre eine Lösung einfach die statischen Aufrufe aus der FilterChain rauszuwerfen, und eine TimeZoneContextFassade als Component zu injecten, die nichts anders macht als an den TimeZoneContext durchzureichen. Dann kannst du die Fassade im Test mocken/mit einer Test-Implementierung austauschen
OK danke. Auf Mock Frameworks wollte ich diesesmal verzichten, da die immer die Tests aufblähen, ich schreibe statt dessen eigene Mock Implementierungen. Werde dann mal die lösung mit der Fassade probieren.
 

Zurück
Oben