如何正确模拟类加载器而不破坏真实的 Thread.currentThread().getContextClassLoader()

问题描述 投票:0回答:1

我有遗留方法 Thread.currentThread() .getContextClassLoader() .getResourceAsStream("security/" + keystoreName),所以当我模拟类加载器时,我正在破坏真正的类加载器,接下来的测试不起作用,所以我有 2 个解决方案,但不知道如何实现它。第一个以某种方式模拟/使用类加载器并首先获得真正的类加载器,并在测试后将真实的类加载器设置回线程。第二个以某种方式创建文件并在测试中使用它,我不知道该怎么做。请帮助解决方案和做什么

实用类的方法

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));
        }

    }

在测试中,我在嘲笑Classloader,但是真正的人停止了他的工作,并且在这个测试之后没有开始测试

java testing junit mockito classloader
1个回答
0
投票

不要嘲笑它。注入一个函数作为参数。

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。

© www.soinside.com 2019 - 2024. All rights reserved.