如何处理 Mock 和 InjectMock
How to deal with Mock and InjectMock
假设我有这个class(为简洁起见省略了一些部分):
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();
}
}
为了测试 Token 是否过期,我创建了另一个 class:
package com.bitbank.utils;
import org.springframework.stereotype.Component;
@Component
public class TimeSource {
public Long getCurrentTimeMillis() {
return System.currentTimeMillis();
}
}
所以,我可以注入假的/旧的 millis 并测试令牌是否已过期。
这是我的 class 测试:
@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));
}
}
但我有几个问题。
使用这种模式(@Mock 和@InjectMocks)我有错误:
base64-encoded secret key cannot be null or empty.`
我知道因为 class 本身是 Mock
ed,所以无法读取测试文件夹中的 application.properties。
如果我使用 @Autowired
:
@SpringBootTest
@TestPropertySource("classpath:application.properties")
class JwtTokenUtilTest {
@Mock
private TimeSource timeSource;
@Autowired
private JwtTokenUtil jwtTokenUtil;
测试本身失败。当然,好像没注入100L的millis
如果我交换 class:
@Autowired
private TimeSource timeSource;
@InjectMocks
private JwtTokenUtil jwtTokenUtil;
我得到:
Cannot invoke "com.bitbank.utils.TimeSource.getCurrentTimeMillis()"
because "this.timeSource" is null
如果你真的想为此使用 @SpringBootTest
那么你需要自动装配 JwtTokenUtil
并使用 @MockBean (**not**
@Mock) for the
TimeSource`。
@SpringBootTest
@TestPropertySource("classpath:application.properties")
class JwtTokenUtilTest {
@MockBean
private TimeSource timeSource;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@MockBean
是一个 Spring 引导注解(利用 Mockito)来指示对于 TimeSource
需要创建一个 mock 并将其用作依赖注入的 bean。这是为了将它与 @SpringBootTest
或来自 Spring 的其他测试注释之一一起使用 Boot like @DataJpaTest
、@WebMvcTest
等
@Mock
是普通的 Mockito 注释,用于在使用 Mockito 进行单元测试时使用。这与 Mockito runner 或 JUnit 的手动初始化模拟的扩展一起使用,但不与 Spring 启动测试注释一起使用(因为它们很高兴地不知道如何处理 @Mock
)。
假设我有这个class(为简洁起见省略了一些部分):
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();
}
}
为了测试 Token 是否过期,我创建了另一个 class:
package com.bitbank.utils;
import org.springframework.stereotype.Component;
@Component
public class TimeSource {
public Long getCurrentTimeMillis() {
return System.currentTimeMillis();
}
}
所以,我可以注入假的/旧的 millis 并测试令牌是否已过期。
这是我的 class 测试:
@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));
}
}
但我有几个问题。
使用这种模式(@Mock 和@InjectMocks)我有错误:
base64-encoded secret key cannot be null or empty.`
我知道因为 class 本身是 Mock
ed,所以无法读取测试文件夹中的 application.properties。
如果我使用 @Autowired
:
@SpringBootTest
@TestPropertySource("classpath:application.properties")
class JwtTokenUtilTest {
@Mock
private TimeSource timeSource;
@Autowired
private JwtTokenUtil jwtTokenUtil;
测试本身失败。当然,好像没注入100L的millis
如果我交换 class:
@Autowired
private TimeSource timeSource;
@InjectMocks
private JwtTokenUtil jwtTokenUtil;
我得到:
Cannot invoke "com.bitbank.utils.TimeSource.getCurrentTimeMillis()" because "this.timeSource" is null
如果你真的想为此使用 @SpringBootTest
那么你需要自动装配 JwtTokenUtil
并使用 @MockBean (**not**
@Mock) for the
TimeSource`。
@SpringBootTest
@TestPropertySource("classpath:application.properties")
class JwtTokenUtilTest {
@MockBean
private TimeSource timeSource;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@MockBean
是一个 Spring 引导注解(利用 Mockito)来指示对于 TimeSource
需要创建一个 mock 并将其用作依赖注入的 bean。这是为了将它与 @SpringBootTest
或来自 Spring 的其他测试注释之一一起使用 Boot like @DataJpaTest
、@WebMvcTest
等
@Mock
是普通的 Mockito 注释,用于在使用 Mockito 进行单元测试时使用。这与 Mockito runner 或 JUnit 的手动初始化模拟的扩展一起使用,但不与 Spring 启动测试注释一起使用(因为它们很高兴地不知道如何处理 @Mock
)。