Search code examples
javajunitjwtjjwt

JJWT token parsing JUnit tests fail with NoSuchMethodError


I have a simple method for validating a JWT using the JJWT library:

    public void validateExpiryDate(String token) {
        try {
            byte[] secret = configProperties.getTokenSecret().getBytes(StandardCharsets.UTF_8);
            SecretKey hmacKey = new SecretKeySpec(secret, HMAC_SHA_256_ALGORITHM);

            Jwts.parserBuilder()
                    .setSigningKey(hmacKey)
                    .build()
                    .parseClaimsJws(token);
        } catch (JwtException e) {
            throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, INVALID_TOKEN_ERR);
        }
    }

It takes the secret from the configuration properties and parses the claims. According to the JJWT documentation, this method should throw a JwtException in case there is anything wrong with the token, and I catch that and re-throw a ResponseStatusException. So, to test this, I have the following test:

    @Test
    void validatorShouldThrow_when_tokenIsExpired() {
        Date issuedAt = Date.from(Instant.now());
        Date expiresAt = Date.from(issuedAt.toInstant().minus(10, ChronoUnit.MINUTES));
        String jwt = generator.createJWT(JWT_TEST_NAME, JWT_TEST_MAIL, JWT_TEST_SUBJECT, JWT_TEST_ID, issuedAt, expiresAt, CONFIG_PROPERTIES_SECRET);

        assertThrows(ResponseStatusException.class, () -> validator.validateExpiryDate(jwt));
    }

The createJWT method is as follows:

    public String createJWT(String name, String email, String subject, String id, Date issuedAt, Date expiresAt, String secret) {
        return Jwts.builder()
                .claim("name", name)
                .claim("email", email)
                .setSubject(subject)
                .setId(id)
                .setIssuedAt(issuedAt)
                .setExpiration(expiresAt)
                .signWith(SignatureAlgorithm.HS256, Base64.getDecoder().decode(secret))
                .compact();
    }

So when I run this test, instead of getting the expected ResponseStatusException, I get a org.opentest4j.AssertionFailedError: Unexpected exception type thrown ==> expected: <org.springframework.web.server.ResponseStatusException> but was: <java.lang.NoSuchMethodError> error, and a bit further down the stacktrace - Caused by: java.lang.NoSuchMethodError: io.jsonwebtoken.Jwts.parserBuilder()Lio/jsonwebtoken/JwtParserBuilder;.

Here are the dependencies I use in the classpath of the class where the validateExpiryDate method is:

    runtimeOnly("io.jsonwebtoken:jjwt-jackson:0.11.5")
    implementation("io.jsonwebtoken:jjwt-api:0.11.5")
    runtimeOnly("io.jsonwebtoken:jjwt-impl:0.11.5")

I ran the gradle -q dependencies command in this class path to make sure there are no outdated dependencies sneaking in somewhere, but all of the jjwt dependencies I see listed are referring to version 0.11.5 so that shouldn't be the issue. Just to make sure it wasn't some cached dependency, I flushed the entire local Maven cache by deleting the repository folder from C:\Users\<user>\.m2

For the love of me, I can't figure out why it can't find the parserBuilder method. I'm fairly certain it has something to do with classpaths and dependencies, but I've tried so many combinations already and can't find the correct setup. Any ideas are welcome!


Solution

  • Turns out I just had to use the exact same dependencies in my main application build file where my tests were, as mentioned in this thread. Before that, I had only the main jjwt dependency - implementation("io.jsonwebtoken:jjwt:0.9.1")

    When I added the below three, the test passes as expected:

        runtimeOnly("io.jsonwebtoken:jjwt-jackson:0.11.5")
        implementation("io.jsonwebtoken:jjwt-api:0.11.5")
        runtimeOnly("io.jsonwebtoken:jjwt-impl:0.11.5")