'How to deal with Mock and InjectMock

Let's say I have this class (omitted some parts for brevity):

package com.bitbank.config;

@Component
public class JwtTokenUtil implements Serializable {
    
    private static final long serialVersionUID = -2338626292552177485L;

    public static final long JWT_TOKEN_VALIDITY = 5L * 60 * 60;
    
    @Value("${jwt.secret}")
    private String secret;
    
    @Autowired
    private TimeSource timeSource;

    public String generateToken(UserDetails userDetails) {
        Map<String, Object> claims = new HashMap<>();
        return doGenerateToken(claims, userDetails.getUsername());
    }

    // while creating the token -
    // 1. Define claims of the token, like Issuer, Expiration, Subject, and the ID
    // 2. Sign the JWT using the HS512 algorithm and secret key.
    // 3. According to JWS Compact
    // Serialization(https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-41#section-3.1)
    // compaction of the JWT to a URL-safe string
    private String doGenerateToken(Map<String, Object> claims, String subject) {

        return Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(new Date(timeSource.getCurrentTimeMillis()))
                .setExpiration(new Date(timeSource.getCurrentTimeMillis() + JWT_TOKEN_VALIDITY * 1000))
                .signWith(SignatureAlgorithm.HS512, secret).compact();
    }

}

To test the expiring of Token, I created another class:

package com.bitbank.utils;

import org.springframework.stereotype.Component;

@Component
public class TimeSource {
    
    public Long getCurrentTimeMillis() {
        return System.currentTimeMillis();
    }
    
}

So, I could inject fake / old millis and test that token is expired.

This is my class test:

@SpringBootTest
@TestPropertySource("classpath:application.properties")
class JwtTokenUtilTest {
    
    @Mock
    private TimeSource timeSource;
    
    @InjectMocks
    private JwtTokenUtil jwtTokenUtil;
    
    @Test
    void canCheckIsExpired() {
        
        when(timeSource.getCurrentTimeMillis()).thenReturn(100L);
        UserDetails userDetails = new User("username", "password", new ArrayList<>());
        String token = jwtTokenUtil.generateToken(userDetails);
        
        assertFalse(jwtTokenUtil.validateToken(token, userDetails));
        
    }
    
}

But I have several issues with it.

With this mode (@Mock and @InjectMocks) I have error:

base64-encoded secret key cannot be null or empty.`

I know because class itself it is Mocked, so cannot read the application.properties in test folder.

If I use @Autowired:

@SpringBootTest
@TestPropertySource("classpath:application.properties")
class JwtTokenUtilTest {
    
    @Mock
    private TimeSource timeSource;
    
    @Autowired
    private JwtTokenUtil jwtTokenUtil;

The test itself fails. Of course, it seems that it didn't inject the 100L millis.

If I swap class:

@Autowired
private TimeSource timeSource;

@InjectMocks
private JwtTokenUtil jwtTokenUtil;

I get:

Cannot invoke "com.bitbank.utils.TimeSource.getCurrentTimeMillis()" because "this.timeSource" is null



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source