我在测试中遇到了模拟类加载器的问题。被测方法使用Thread.currentThread().getContextClassLoader()来加载资源,当我模拟ClassLoader时,它破坏了真正的ClassLoader,导致后续测试失败。我正在寻找一种模拟或使用类加载器而不破坏真实类加载器的方法。具体来说,我想要么在测试前获取真实的ClassLoader并在测试后将其设置回来,要么想办法创建一个文件并在测试中使用它而不影响真实的ClassLoader。我如何重构我的测试来实现这一目标?
实用类中的方法
public static KeyPair getKeyPairFromKeystore(JwtConfigProperties jwtConfigProperties,
KeystoreConfigProperties keystoreConfigProperties) {
log.info( "Called get keypair from keystore");
KeyStore.PrivateKeyEntry privateKeyEntry = null;
Certificate cert = null;
String keystoreName = keystoreConfigProperties.getName();
try (InputStream in = Thread.currentThread()
.getContextClassLoader()
.getResourceAsStream("security/" + keystoreName)
) {
if (in == null) {
log.info("Input file is null!");
throw new NoSuchElementException("Input file is null");
}
char[] keystorePassword = keystoreConfigProperties.getPassword().toCharArray();
char[] jwtPassword = jwtConfigProperties.getPassword().toCharArray();
String jwtAlias = jwtConfigProperties.getAlias();
//enforcing jceks
KeyStore keyStore = KeyStore.getInstance("JCEKS");
keyStore.load(in, keystorePassword);
KeyStore.PasswordProtection keyPassword = new KeyStore
.PasswordProtection(jwtPassword);
privateKeyEntry = (KeyStore.PrivateKeyEntry) keyStore.getEntry(jwtAlias, keyPassword);
cert = keyStore.getCertificate(jwtAlias);
log.debug("Public key: {}", cert.getPublicKey().toString());
} catch (KeyStoreException | IOException | NoSuchAlgorithmException
| CertificateException | UnrecoverableEntryException e) {
log.error("Error message: {}, Exception: {}", e.getMessage(), e);
}
return new KeyPair(Objects.requireNonNull(cert).getPublicKey(),
Objects.requireNonNull(privateKeyEntry).getPrivateKey());
}
JwtConfigProperties 和 KeystoreConfigProperties 它是来自 application.yml 的配置属性
我的测试
@RunWith(MockitoJUnitRunner.class)
public class KeystoreUtilTest {
@Mock
private ClassLoader newLoader;
@Mock
private InputStream in;
@Mock
private KeyStore keystore;
@Test
public void testGetKeyPairFromKeystore_validPublicKey() throws KeyStoreException {
Certificate certificate = mock(Certificate.class);
PublicKey publicKey = mock(PublicKey.class);
JwtConfigProperties jwtConfigProperties = new JwtConfigProperties();
jwtConfigProperties.setAlias("alias");
jwtConfigProperties.setPassword("password");
KeystoreConfigProperties keystoreConfigProperties = new KeystoreConfigProperties();
keystoreConfigProperties.setName("name");
keystoreConfigProperties.setPassword("password");
Thread.currentThread().setContextClassLoader(newLoader);
when(newLoader.getResourceAsStream("security/" + keystoreConfigProperties.getName())).thenReturn(in);
try (MockedStatic<KeyStore> keyStoreMockedStatic = mockStatic(KeyStore.class)) {
keyStoreMockedStatic
.when(() -> KeyStore.getInstance("JCEKS"))
.thenReturn(keystore);
keyStoreMockedStatic
.when(() -> KeyStore.getInstance("JCEKS").getCertificate("alias"))
.thenReturn(certificate);
keyStoreMockedStatic
.when(() -> KeyStore.getInstance("JCEKS").getCertificate("alias").getPublicKey())
.thenReturn(publicKey);
assertThrows(NullPointerException.class,
() -> KeystoreUtil.getKeyPairFromKeystore(jwtConfigProperties, keystoreConfigProperties));
verify(keystore, times(2)).getCertificate("alias");
keyStoreMockedStatic
.verify(() -> KeyStore.getInstance("JCEKS"), times(3));
}
}
在测试中,我正在嘲笑类加载器,但是真正的类加载器停止了他的工作,并且在这次测试之后测试没有开始
不要嘲笑它。注入函数作为参数。
public static KeyPair getKeyPairFromKeystore(JwtConfigProperties jwtConfigProperties,
KeystoreConfigProperties keystoreConfigProperties,
Function<String, InputStream> getInputStream
) {
//...
try (InputStream in = getInputStream.apply("security/" + keystoreName)) {
//...
}
}
在你的真实代码中,传递一个函数
getKeyPairFromKeystore(..., ...,
str -> Thread.currentThread()
.getContextClassLoader()
.getResourceAsStream(str)
)
在您的测试中,传递任何返回您想要测试的 InputStream 的函数。