Home > Enterprise >  How to deal with Mock and InjectMock
How to deal with Mock and InjectMock

Time:02-11

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

CodePudding user response:

If you really want to use @SpringBootTest for this then you need to autowire the JwtTokenUtil and use @MockBean (**not** @Mock) for the TimeSource`.

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

@MockBean is a Spring Boot annotation (leveraging Mockito) to indicate that for TimeSource a mock needs to be created and used as a bean for dependency injection. This is for using it with @SpringBootTest or one of the other test annotations from Spring Boot like @DataJpaTest, @WebMvcTest etc.

@Mock is the plain Mockito annotation that is for use when using a unit test with Mockito. This is used in conjunction with the Mockito runner or extension for JUnit of manual initialization of the mocks but not with the Spring Boot test annotations (as those are blissfully unaware of what to do with @Mock).

  • Related