简体   繁体   中英

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

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:

  • Calling the method and verifying that the result is a non-empty string
  • Calling the method and verifying that the result is a valid base64 string
  • Calling the method two times and asserting that the result is different for both calls (real randomness allows the method to return the same output two times in a row, but the chances are really, really slim for 32 bytes of random data).

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM