6

I am trying to mock the class KeyStore. After mocking I do not want anything to happen when the load method if it has been called. Therefore I wrote the below lines to achieve this.

        @PrepareForTest(KeyStoreFactory.class)
        @Test
        public void should_verify_signature_when_verifySignature_called_with_fileName_and_certificate_details_in_verifySignature_method() throws Exception {
            PowerMockito.mockStatic(KeyStoreFactory.class);

            KeyStore keyStoreMock = PowerMockito.mock(KeyStore.class);
            PowerMockito.when(KeyStoreFactory.getInstance(anyString(), anyString())).thenReturn(keyStoreMock);
            Mockito.doNothing().when(keyStoreMock).load(Mockito.any(InputStream.class), Mockito.any(char[].class));
            Certificate certificateMock = Mockito.mock(Certificate.class);
            when(keyStoreMock.getCertificate(anyString())).thenReturn(certificateMock);
            boolean result = signatureUtil.verifySignature("src//test//java//Updates.zip.signed.pkcs7"
                    , "src//test//java//Updates-retrieved.zip", "Windows-MY,SunMSCAPI,someName");
            Assert.assertTrue(result);

        }

But the load method was throwing null pointer exception. Then when I debug I found out that the real method is getting called though I have specified for mockito to not to. What am I doing wrong here? Please advice.

Below is the method for which I'm writing the test.

    @Override
        public boolean verifySignature(String filePath, String extractContentsPath, String csvParams)
                throws ServiceSDKException {
            boolean result = false;
            String typeOfCertificateStore = "";
            String certificateStoreProvider = "";
            String certificateName = "";
            SignerInformationVerifier verifier = null;
            if (filePath != null && extractContentsPath != null && csvParams != null && !filePath.isEmpty()
                    && !extractContentsPath.isEmpty() && !csvParams.isEmpty()) {

                try {
                    String[] receivedParams = csvParams.split(",");
                    typeOfCertificateStore = receivedParams[0];
                    certificateStoreProvider = receivedParams[1];
                    certificateName = receivedParams[2];
                } catch (ArrayIndexOutOfBoundsException e) {
                    throw new ServiceSDKException("csvParams should have type of certificate store, certificate store provider and certificate name respectively", e);
                }

                try {
                    Path signedDataFilePath = Paths.get(filePath);
                    Path pathToExtractContents = Paths.get(extractContentsPath);

                    KeyStore msCertStore = KeyStoreFactory.getInstance(typeOfCertificateStore, certificateStoreProvider);
                    msCertStore.load(null, null);
                    try {
                        verifier = new JcaSimpleSignerInfoVerifierBuilder()
                                .setProvider(certificateStoreProvider)
                                .build(((X509Certificate) msCertStore.getCertificate(certificateName)));
                    } catch (Exception e) {
                        throw new ServiceSDKException("Exception occurred when building certificate",e);
                    }
                    verify(signedDataFilePath, pathToExtractContents, verifier);
                    result = true;
                } catch (IOException | NoSuchAlgorithmException
                        | CertificateException e) {
                    result = false;
                    throw new ServiceSDKException("Exception occurred while preparing to verify signature " , e);
                }
            } else {
                throw new ServiceSDKException("FilePath,extract contents path or csv params cannot be empty or null");
            }
            return result;
        }

This is the entire test class :

@RunWith(PowerMockRunner.class)
public class SignatureUtilImplTest {

    SignatureUtilImpl signatureUtil = new SignatureUtilImpl();

@PrepareForTest({KeyStoreFactory.class, SignatureUtilImpl.class})
    @Test
    public void should_verify_signature_when_verifySignature_called_with_fileName_and_certificate_details_in_verifySignature_method() throws Exception {
        CMSSignedDataParser spMock = PowerMockito.mock(CMSSignedDataParser.class);
        SignerInformationVerifier verifierMock = Mockito.mock(SignerInformationVerifier.class);
        SignatureUtilImpl signatureUtilSpy = Mockito.spy(new SignatureUtilImpl());
        KeyStore keyStoreMock = PowerMockito.mock(KeyStore.class);
        PowerMockito.mockStatic(KeyStoreFactory.class);
        PowerMockito.when(KeyStoreFactory.getInstance(anyString(), anyString())).thenReturn(keyStoreMock);
        SignerInformation signerInformationMock = Mockito.mock(SignerInformation.class);
        Collection<SignerInformation> collection = new ArrayList();
        collection.add(signerInformationMock);

        Mockito.doCallRealMethod().when(signatureUtilSpy).verifySignature("src/test/java/Updates.zip.signed.pkcs7"
                , "src/test/java/Updates-retrieved.zip", "Windows-MY,SunMSCAPI,someName");
        Mockito.doNothing().when(signatureUtilSpy).loadKeyStore();
        Mockito.doReturn(verifierMock).when(signatureUtilSpy).getSignerInformationVerifier(anyString(), anyString());
        Mockito.doReturn(spMock).when(signatureUtilSpy).getDataParser(any(DigestCalculatorProvider.class), any(FileInputStream.class));
        Mockito.doReturn(collection).when(spMock).getSignerInfos().getSigners();
        Mockito.doReturn(true).when(signerInformationMock).verify(verifierMock);
        //PowerMockito.doNothing().when(signatureUtilSpy, "verify", any(Path.class),any(Path.class),any(SignerInformationVerifier.class));

//        PowerMockito.doReturn(true).when(signatureUtilSpy, PowerMockito.method(SignatureUtilImpl.class, "verify",Path.class,Path.class, SignerInformationVerifier.class))
//                .withArguments(any(Path.class),any(Path.class),any(SignerInformationVerifier.class));

        boolean result = signatureUtilSpy.verifySignature("src/test/java/Updates.zip.signed.pkcs7"
                , "src/test/java/Updates-retrieved.zip", "Windows-MY,SunMSCAPI,someName");
        Assert.assertTrue(result);
}

}
AnOldSoul
  • 4,017
  • 12
  • 57
  • 118
  • I assume signatureUtil is internally calling KeyStoreFactory method. Mock that particular method in signatureUtil that uses KSF. Can you share the signatureUtil source – Sanj Apr 04 '16 at 04:48
  • I added the code you requested. I need to add the behavior mock for the method msCertStore.load(null,null); How can I do it? – AnOldSoul Apr 04 '16 at 04:52
  • Mockito.doNothing().when(KeyStore.load(Mockito.any(InputStream.class), Mockito.any(char[].class))) – Sanj Apr 04 '16 at 05:05
  • That cannot be done, because load method is a nonstatic method. Its not static :( – AnOldSoul Apr 04 '16 at 05:16

4 Answers4

2

I think this can help :

Create another method in SignatureUtil:

public KeyStore loadKeyStore(...){
  KeyStore msCertStore = KeyStoreFactory.getInstance(typeOfCertificateStore,certificateStoreProvider);
  msCertStore.load(null, null);
}

In your test class do it like below.

Mockito.doNothing().when(signatureUtilMock).loadKeyStore(anyString(), anyString());

Here is the code that is working for me

package com.foo;

import java.io.IOException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;


public class KeyStoreService {
  public KeyStoreService(){

  }

  public void load() throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException{
    System.out.println("start");
    KeyStore ks = KeyStore.getInstance("");
    ks.load(null, null);
    System.out.println("end");
 }


}

Test Class

package com.foo.test;

import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.cert.CertificateException;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Matchers;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

import com.foo.KeyStoreService;

@PrepareForTest(KeyStoreService.class)
@RunWith(PowerMockRunner.class)
public class TestKeyStore {

    @Test
    public void test1() throws KeyStoreException, NoSuchProviderException, NoSuchAlgorithmException, CertificateException, IOException{
        PowerMockito.mockStatic(KeyStore.class);
        KeyStore keyStoreMock = PowerMockito.mock(KeyStore.class);
        KeyStoreService kss = new KeyStoreService();
        PowerMockito.when(KeyStore.getInstance(Matchers.anyString(), Matchers.anyString())).thenReturn(keyStoreMock);
        Mockito.doNothing().when(keyStoreMock).load(Mockito.any(InputStream.class), Mockito.any(char[].class));
        kss.load();
    }
}
Sanj
  • 3,879
  • 2
  • 23
  • 26
  • 1
    In that case, I will have keep creating methods for each system class method I want to mock right? Also its interface might not be needed to be public as well. So is it not like we are doing things in code just for testing purposes? – AnOldSoul Apr 04 '16 at 05:41
  • you can make the interface as private and mock it. – Sanj Apr 04 '16 at 05:43
  • If I make the loadKeyStore method private, it will not be visible for me to mock it? – AnOldSoul Apr 04 '16 at 05:57
  • http://stackoverflow.com/questions/28121177/mock-private-method-using-powermockito – Sanj Apr 04 '16 at 06:08
  • Tried this, PowerMockito.doReturn(true).when(signatureUtilSpy, "verify", any(Path.class),any(Path.class),any(SignerInformationVerifier.class)); but its calling the actual method! shouldn't it just return true without executing the real method? – AnOldSoul Apr 04 '16 at 07:34
  • Are you running your test with PowerMockRunner. If not add @RunWith(PowerMockRunner.class) to your tests. – Sanj Apr 04 '16 at 17:15
  • Yes I was running it with @RunWith – AnOldSoul Apr 05 '16 at 00:55
  • Will it be possible to share entire test class ? – Sanj Apr 05 '16 at 00:56
  • Please refer the edited question for the entire test class – AnOldSoul Apr 05 '16 at 02:14
  • I have added code for KeyStore.load. It is working for me. Let me know if this works for you. – Sanj Apr 05 '16 at 02:36
2

KeyStore is a system class, so to mock it the ClassThatCallsTheSystemClass should be added to @PrepareForTest. As I understand in your case the SignatureUtilImpl should be in @PrepareForTest. But may be another classes is also called this system class.

By the way, due to how PowerMock mocks system class you may have trouble with cast in this line

new JcaSimpleSignerInfoVerifierBuilder()
                            .setProvider(certificateStoreProvider)
                            .build(((X509Certificate) msCertStore.getCertificate(certificateName)));

More information you may find here and here

Artur Zagretdinov
  • 2,034
  • 13
  • 22
0

The following code works for me

PowerMockito.mockStatic(KeyStore.class);
KeyStore mockKeyStore = PowerMockito.mock(KeyStore.class);

PowerMockito.doNothing().when(mockKeyStore).load(Matchers.any(), Matchers.anyObject());
     Mockito.when(KeyStore.getInstance(Matchers.anyString())).thenReturn(mockKeyStore);
iFreilicht
  • 13,271
  • 9
  • 43
  • 74
0

doNothing() method will calling actual method, if you don't want call, you can use suppress-method.it will suppress no-return method to call.

doNothing().when(switchMock).finalVoidMethod(channelId);

change

suppress(method(SignatureUtilImpl.class, "loadKeyStore"));
Marx.Luo
  • 49
  • 5