Mockito.when mit JDK 21 verursacht Buildfehler "RestrictedIdentifierWhen"

Robertop

Bekanntes Mitglied
Guten Morgen zusammen,

ich beschäftige mich gerade ein wenig mit Java 21 und da bin ich jetzt bei unseren Tests auf ein merkwürdiges Verhalten gestoßen, dass ich auch in einem neuen ganz einfachen Maven Projekt nachstellen konnte sobald ich dort Java 21 eintrage.

Ich habe ein neues Maven Projekt in Eclipse angelegt, dass nur die nötigsten Sachen enthält:
XML:
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>test.test</groupId>
    <artifactId>Calculator</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>21</maven.compiler.source>
        <maven.compiler.target>21</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-core</artifactId>
            <version>5.7.0</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

Mein Projekt enthält eine Adder Klasse, die eine add(int, int) Funktion hat und eine Calculator Klasse, die eine Funktion add(Integer, Integer) hat, die intern den Adder verwendet. Das habe ich so aufgebaut, damit ich etwas zu mocken habe.

In meiner Testfunktion habe ich jetzt folgendes merkwürdiges Verhalten: Der Aufruf von when verursacht 2 Buildfehler, weil
  1. thenReturn angeblich nicht für primitive Typen verwendet werden kann
  2. Der when Aufruf angeblich an einer ungültigen Stelle steht.
Das merkwürdige daran ist, dass alles gut ist, wenn ich stattdessen Mockito.when vewende, obwohl das aus meiner Sicht doch eigentlich das gleiche sein sollte.
1699604215704.png
--------------------------------------------------------------------------
1699604406685.png
 
Zuletzt bearbeitet:

KonradN

Super-Moderator
Mitarbeiter
Das Problem wird vermutlich sein, dass es mit Java 21 auch ein when gibt (in Zusammenhang mit switch):
Pattern Matching for switch Expressions and Statements (oracle.com)

Wobei ich das als einen Fehler ansehen würde, denn when ist nur im Kontext eines switch guards ein Keyword.
Siehe dazu:
Chapter 3. Lexical Structure (oracle.com)
During the reduction of input characters to input elements (§3.5), a sequence of input characters that notionally matches a contextual keyword is reduced to a contextual keyword if and only if both of the following conditions hold:
  • For when, when recognized as a terminal in a Guard (§14.11.1).

Und der static Import muss da gewesen sein, sonst
  • wäre die Meldung eine andere.
  • der import wird im zweiten Fall angemeckert in einer Warning

Also die Meldung scheint mir etwas so, dass er hier das, was hinter dem where kommt, als Bedingung auswerten will und dann hat man in den Klammern erst den Ausdruck addMock.add(...) was ein int zurück gibt und darauf kann man nun einmal nichts aufrufen.
 

KonradN

Super-Moderator
Mitarbeiter
Welches Java 21 verwendest Du? Evtl. mal eine andere Implementation probieren? Ggf. gibt es hier auch schon einen Bug-Report. Danach habe ich aber jetzt erst einmal nicht gesucht ...
 

Robertop

Bekanntes Mitglied
Danke für die schnellen Rückmeldungen.

Bei dem Workspace verwende ich gerade das Adoptium JDK 21. Ich werde gleich mal andere Versionen ausprobieren.
 

KonradN

Super-Moderator
Mitarbeiter
Hat Eclipse nicht seinen eigenen Compiler? Ich würde das dann eher als Eclipse Bug sehen.

Aber der Eclipse Compiler hinkt doch immer hinterher und wird daher Java 21 noch nicht können...
Das war damals meine Erkenntnis, als ich den einmal verwenden wollte. Aber evtl. habe ich auch einfach nur was falsch gemacht...

(Heya - Ich schreibe etwas zu Eclipse ohne in den Bashing Modus zu verfallen ... Hätte ich mir selbst gar nicht zugetraut :) )
 

Robertop

Bekanntes Mitglied
Gegen die Theorie, dass Eclipse schuld ist, spricht , dass, wenn ich unabhängig von Eclipse mvn verify ausführe, das selbe Problem auftritt:

Der Build mit dem "Mockito.when" Aufruf funktioniert. Beim Build der Version ohne "Mockito." erhalte ich diesen Fehler:
Code:
Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0.64 sec <<< FAILURE!
testAdd(test.test.CalculatorTest)  Time elapsed: 0.083 sec  <<< ERROR!
java.lang.Error: Unresolved compilation problems:
        Syntax error on token "RestrictedIdentifierWhen", delete this token
        Cannot invoke thenReturn(int) on the primitive type int
 
Zuletzt bearbeitet:

Robertop

Bekanntes Mitglied
Wie es aussieht war doch Eclipse und Maven das Problem, bzw. eher die Versionen, die ich jeweils verwendet hatte:

Ich habe mir gerade die ganz aktuelle Maven Version 3.9.5 herunterladen. Damit läuft der Build richtig.
Dann habe ich mir das aktuelle Eclipse 2023-12 M2 heruntergeladen. Hier wird der Fehler nicht mehr angezeigt.

Jetzt war die Lösung ja doch einfacher, als ich erwartet habe. 😅 Vorher hatte ich Maven 3.8.4 und Eclipse 2023-12 M1.
 

LimDul

Top Contributor
Gegen die Theorie, dass Eclipse schuld ist, spricht , dass, wenn ich unabhängig von Eclipse mvn verify ausführe, das selbe Problem auftritt:

Der Build mit dem "Mockito.when" Aufruf funktioniert. Beim Build der Version ohne "Mockito." erhalte ich diesen Fehler:
Code:
Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0.64 sec <<< FAILURE!
testAdd(test.test.CalculatorTest)  Time elapsed: 0.083 sec  <<< ERROR!
java.lang.Error: Unresolved compilation problems:
        Syntax error on token "RestrictedIdentifierWhen", delete this token
        Cannot invoke thenReturn(int) on the primitive type int
Das ist immer noch der Eclipse Compiler - wenn müsstest du mit Maven mal kompilieren. Ich gehe davon aus, dass Eclipse das Problem war.
 

Robertop

Bekanntes Mitglied
Das verstehe ich nicht ganz. Wenn ich im Windows Terminal mvn verify aufrufe, wieso wird der Eclipse Compiler dann verwendet? Und wieso hat es dann mit der neuen Maven Version aber noch ohne das neue Eclipse funktioniert?
 

LimDul

Top Contributor
Vermutlich hat mvn keine Änderungen gefunden und daher nicht neu gebaut. mvn clean verify wäre der Trick gewesen. Unresolved compilation problems ist eine Exception die vom Eclipse Compiler kommt, nicht von javac
 

KonradN

Super-Moderator
Mitarbeiter
@LimDul Da Du da ja näheren Einblick hast: Mich würde das im Detail einmal interessieren.

Der Eclipse Java Compiler ist ja Teil vom JDT Core. Aber der Compiler ist doch noch gar nicht so weit, auch Java 21 zu unterstützen. Leider findet sich da nur sehr schwer etwas sinnvolles an Informationen, was ich nicht gut finde.

So habe ich unter anderem gefunden:
Planning Java Releases · eclipse-jdt/eclipse.jdt.core Wiki (github.com)

Da ist man derzeit bei Java 19. Aber ist das aktuell? Auch wenn man bei JDT Core Component | The Eclipse Foundation herum schaut, dann wird man nicht wirklich fündig.

Aber generell sind das ja ganz viele Komponenten, die da zusammen kommen. Und das, was etwas aktuell zu sein scheint, ist natürlich die Unterstützung von Java 21 im Editor mit Hinweisen. Ich vermute, dass da natürlich Java 21 mit unterstützt wird. Und da war dann vermutlich der Bug, dass da dann etwas falsch ausgewertet wurde und die Fehlermeldung war dann vom Eclipse JDT Plugin?

Oder ist die ganze Dokumentation schlicht veraltet und das JDT kann auch Java 21 komplett übersetzen? Als ich vor einiger Zeit einmal das Eclipse JDT in IntelliJ einbinden wollte, hat es schlicht aktuelle releases nicht unterstützt und da hatte ich den letzten Download verwendet. Daher irritiert mich das alles etwas.
 

LimDul

Top Contributor
Spannend. Die Unresolved Compilation Exception kann eigentlich nur von Eclipse kommen. Soweit mir Google gerade sagt, kann Eclipse out of the Box kein Java 21. ich hab nur den Marketplace Eintrag gefunden: https://marketplace.eclipse.org/content/java-21-support-eclipse-2023-09-429

Ich behaupte, das Beispiel vom Thread-Ersteller wurde - obwohl in Maven so eingestellt - von Eclipse gar nicht gegen Java 21 kompiliert, sondern gegen Java 17, 18 oder 19
 

KonradN

Super-Moderator
Mitarbeiter
Spannend. Die Unresolved Compilation Exception kann eigentlich nur von Eclipse kommen. Soweit mir Google gerade sagt, kann Eclipse out of the Box kein Java 21. ich hab nur den Marketplace Eintrag gefunden: https://marketplace.eclipse.org/content/java-21-support-eclipse-2023-09-429
Danke für den Link, den hatte ich nicht gefunden.
Ich behaupte, das Beispiel vom Thread-Ersteller wurde - obwohl in Maven so eingestellt - von Eclipse gar nicht gegen Java 21 kompiliert, sondern gegen Java 17, 18 oder 19
Ja, das würde auch meine Verwirrung erklären. Und die where clause war bekannt, weil es in 19 schon als Preview vorhanden war meine ich. Also irgend so ein Zusammenspiel könnte dann sein.

Aber ich habe immer Bauchschmerzen, wenn man Maven mit anderen IDE Internas mixt. Da muss man - meine ich - immer gut aufpassen. Das ist auch keine Besonderheit bei Eclipse sondern eigentlich bei jeder Entwicklungsumgebung, die ich kenne.

@Robertop Unabhängig, ob die Vermutungen korrekt sind oder nicht: Ich würde Dir raten, auch in Eclipse dann immer den Maven Build laufen zu lassen (Das geht da über Run -> Maven oder so ähnlich ... und dann als goal "clean package" oder so angeben oder falls es fertig Einträge gibt, diese dann auswählen).
Und bei Maven: Zur Not auch auf der Kommandozeile laufen lassen. Das ist etwas, das ich regelmäßig prüfe: Einfach einmal ein ./mvnw clean package laufen lassen.

Dazu einfach der wichtige Hinweis: Du brauchst auf dem System nur das passende Java. Du brauchst kein Maven! Das mache ich immer per Wrapper. Dazu kann man auch in der Entwicklungsumgebung das goal wrapper:wrapper laufen lassen. (Je nach Konfiguration von Maven kennt er das nicht - dann das Wrapper Plugin hinzu fügen:
Java:
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-wrapper-plugin</artifactId>
                <version>${maven.wrapper.plugin}</version>
            </plugin>
Die Version war zuletzt 3.2.0.
==> Damit kannst Du einfach mit mvnw Maven aufrufen und die korrekte Version wird dann - falls notwendig - automatisch herunter geladen und ausgeführt.
 

Robertop

Bekanntes Mitglied
Vermutlich hat mvn keine Änderungen gefunden und daher nicht neu gebaut. mvn clean verify wäre der Trick gewesen. Unresolved compilation problems ist eine Exception die vom Eclipse Compiler kommt, nicht von javac
Ich habe das tatsächlich nachher noch mit dem Maven 3.8.4 ausprobiert, aber dann mvn clean verify gemacht. Damit hat das dann tatsächlich geklappt.

Den Maven Build in der IDE hatte ich tatsächlich ganz am Anfang, als mir das beim ersten Testen aufgefallen ist, auch einmal ausgeführt und festgestellt, dass er nur funktionierte, wenn man skipTests=true mitgegeben hat. Bei genaurer Betrachtung ist mir jetzt aber auch aufgefallen, dass die Tests beim Maven Build nicht wegen einem Buildfehler beim when fehlgeschlagen sind, sondern wegen einem anderen Buildfehler, der mir vorher in der IDE wegen der ganzen When-Exceptions nicht aufgefallen war. 🤦‍♂️

Spannend. Die Unresolved Compilation Exception kann eigentlich nur von Eclipse kommen. Soweit mir Google gerade sagt, kann Eclipse out of the Box kein Java 21. ich hab nur den Marketplace Eintrag gefunden: https://marketplace.eclipse.org/content/java-21-support-eclipse-2023-09-429
Genau deshalb hatte ich ja eigentlich auf 2023-12 M1 gewartet, bevor ich irgendwas mit Java 21 ausprobiere. War wohl immernoch ein wenig zu voreilig. 😂
 

Ähnliche Java Themen

Neue Themen


Oben