Home > Software engineering >  A mocked static class is always returning null, am I doing something wrong?
A mocked static class is always returning null, am I doing something wrong?

Time:09-07

I'm making a unit test on a method that uses two static classes. So, inside the test I have this

try(MockedStatic<SecurityEncryption> securityEncryption = mockStatic(SecurityEncryption.class);
        MockedStatic<SecretUtils> secretUtils = mockStatic(SecretUtils.class)
    ){
        secretUtils.when(() -> SecretUtils.getSecret(anyString(), anyString())).thenReturn("secreto");
        securityEncryption.when(() -> SecurityEncryption.desencriptarString(anyString(), anyString())).thenReturn("texto");
        assertEquals("texto", SecurityEncryption.desencriptarString("", ""));
        notificationSendEmailService.sendNotificationEmail("", "", "");
    }

The first class, SecretUtils is returning the mocked value, "secreto". But, the second one is always null, no matter what I do, always null inside the class that I'm testing, notificationSendEmail.

I thought the class was not mocked, but the assertion passes, so is inside the method that is not working, also I print the getSecret method inside my sendNotificationEmail and is "secreto", the other one, desencriptarString is always null.

What I'm doing wrong? D:

The service I'm testing has @InjectMocks annotation, and in @BeforeEach method is the MockitoAnnotations.openMocks(this) line.

I don't want to use PowerMockito btw.

CodePudding user response:

Would you mind sharing a bit more code? Because the problem is not reproducible. The following minimal but representative example works in my environment. I hope it helps.

UPD: Most likely your version of NotificationSendEmailService calls an overloaded version of SecurityEncryption.desencriptarString that has a different signature, i.e. different arguments instead of two String arguments. And that version is not mocked in your test. Anyway, a bit more details of NotificationSendEmailService (and others) will really help.

  • NotificationSendEmailService
public class NotificationSendEmailService {
    void sendNotificationEmail(String a, String b, String c) {
        var secreto = SecretUtils.getSecret(a, b);
        System.out.println(secreto);
        assert "secreto".equals(secreto); // throws here if not properly mocked

        var texto = SecurityEncryption.desencriptarString(b, c);
        System.out.println(texto);
        assert "texto".equals(texto); // throws here if not properly mocked
    }
}
  • SecretUtils
public class SecretUtils {
    static String getSecret(String a, String b) {
        throw new UnsupportedOperationException("Not thrown if properly mocked in test");
    }
}
  • SecurityEncryption
public class SecurityEncryption {
    static String desencriptarString(String b, String c) {
        throw new UnsupportedOperationException("Never thrown when properly mocked in test");
    }
}
  • test/resource/mockito-extensions/org.mockito.plugins.MockMaker

This can also be configured differently.

mock-maker-inline
  • NotificationSendEmailServiceTest

PASSES as both classes are properly mocked. Otherwise it would have failed with UnsupportedOperationException.

public class NotificationSendEmailServiceTest {

    @Test
    void sendNotificationEmail() {
        var notificationSendEmailService = new NotificationSendEmailService();

        // the following is copy-pasted from the original question
        try (MockedStatic<SecurityEncryption> securityEncryption = mockStatic(SecurityEncryption.class);
             MockedStatic<SecretUtils> secretUtils = mockStatic(SecretUtils.class)
        ) {
            secretUtils.when(() -> SecretUtils.getSecret(anyString(), anyString())).thenReturn("secreto");
            securityEncryption.when(() -> SecurityEncryption.desencriptarString(anyString(), anyString())).thenReturn("texto");

            assertEquals("texto", SecurityEncryption.desencriptarString("", ""));

            notificationSendEmailService.sendNotificationEmail("", "", "");
        }
    }
}
  • Related