javamockitopowermockitotestng.xml

Nullpointerexception in when of testNG testing method


I have to test a method with testNG framework:

public class PkceUtil {

    private PkceUtil(){
        super();
    }

    /**
     * External application use this method for generating a Base64 URL encoded alphanumeric string of characters
     * with a minimum length of 43 characters and a maximum length of 128 characters (code verifier).
     * @return code verifier
     */
    public static String generateCodeVerifier(){
        SecureRandom secureRandom = new SecureRandom();
        byte[] codeVerifier = new byte[32];
        secureRandom.nextBytes(codeVerifier);
        return Base64.getUrlEncoder().withoutPadding().encodeToString(codeVerifier);
    }
..

But i get a Nullpointer in when(Base64.getUrlEncoder()...

import org.mockito.MockedStatic;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.testng.PowerMockTestCase;
import org.testng.annotations.Test;

import java.util.Base64;

import static org.mockito.Mockito.*;
import static org.testng.Assert.assertEquals;

@PrepareForTest(Base64.class)
public class PkceUtilTest extends PowerMockTestCase {


    @Test
    public void testGenerateCodeVerifier() {
        MockedStatic<Base64> mockedStaticBase64 = mockStatic(Base64.class);
        String codeVerifier= "";
        23: -->when(Base64.getUrlEncoder().withoutPadding().encodeToString(any())).thenReturn(codeVerifier);
        final String codeVerifierReturned = PkceUtil.generateCodeVerifier();
        
        assertEquals(codeVerifierReturned, codeVerifier);
        verify(Base64.getUrlEncoder().withoutPadding().encodeToString(any()));
        
        mockedStaticBase64.close();
    }
}

java.lang.NullPointerException at com.cadit.oidc.sdk.pkce.PkceUtilTest.testGenerateCodeVerifier(PkceUtilTest.java:23) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:132) at org.testng.internal.TestInvoker.invokeMethod(TestInvoker.java:599) at org.testng.internal.TestInvoker.invokeTestMethod(TestInvoker.java:174) at org.testng.internal.MethodRunner.runInSequence(MethodRunner.java:46) at org.testng.internal.TestInvoker$MethodInvocationAgent.invoke(TestInvoker.java:822) at org.testng.internal.TestInvoker.invokeTestMethods(TestInvoker.java:147) at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:146) at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:128) at java.util.ArrayList.forEach(ArrayList.java:1257) at org.testng.TestRunner.privateRun(TestRunner.java:764) at org.testng.TestRunner.run(TestRunner.java:585) at org.testng.SuiteRunner.runTest(SuiteRunner.java:384) at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:378) at org.testng.SuiteRunner.privateRun(SuiteRunner.java:337) at org.testng.SuiteRunner.run(SuiteRunner.java:286) at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:53)

 <dependency>
                <groupId>org.testng</groupId>
                <artifactId>testng</artifactId>
                <version>7.3.0</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.powermock</groupId>
                <artifactId>powermock-module-testng</artifactId>
                <version>2.0.7</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.mockito</groupId>
                <artifactId>mockito-core</artifactId>
                <version>3.5.13</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.mockito</groupId>
                <artifactId>mockito-inline</artifactId>
                <version>3.5.13</version>
                <scope>test</scope>
            </dependency>

What's wrong with my test code?

thanks


Solution

  • You have to ask yourself: what is your test actually testing? I'd say: nothing. Everything important is mocked out and the method simply returns what you tell the mocked object to return. So in effect, you are testing that Mockito/PowerMock does its job properly.

    More useful tests could be:

    NB Do not create a new instance of SecureRandom every time your method is called; create it once when the class is loaded and reuse that instance.

    Or even better: inject the random instance, so you can replace it with a test double when testing. That way you have full control over the generated random value and can thus assert the method's output.


    Ignoring that, back to your question:

    Base64.getUrlEncoder() is never set up to return anything but the default. The default for non-setup'ed methods is … null mostly (with some exceptions, such as empty collections).

    Either manually stub every call in your chain, or specify Answers.RETURNS_DEEP_STUBS (so every call would return another call).

    Also, if your test throws an exception, your mockStatic resource is never closed. It's better to use the try-with-resources pattern when working with AutoCloseable instances: it handles all the error conditions properly and closes your resource under any circumstances:

    try (MockedStatic<Base64> mockedStaticBase64 = mockStatic(Base64.class)) {
        String codeVerifier= "";
        when(Base64.getUrlEncoder().withoutPadding().encodeToString(any())).thenReturn(codeVerifier);
        final String codeVerifierReturned = PkceUtil.generateCodeVerifier();
        
        assertEquals(codeVerifierReturned, codeVerifier);
        verify(Base64.getUrlEncoder().withoutPadding().encodeToString(any()));   
    }
    

    One question though: why do you want to mock the UrlEncoder to always return an empty string? If it is passed an empty string, it should already return one. What do you gain by mocking it (and then verifying that the mock has been called)?

    PS. Everytime a mock returns a mock a fairy dies