mardi 1 mars 2016

Testing a method which depends on private members that cannot be injected or passed as parameter

I have the below method which depends only on private members. This is a class that is run by a 3rd party library. I only need to specify the name of the class and it will create an instance of the class and run it. I don't want to pass these private variables through constructor as it doesn't make any sense. But to verify whether the methods that should be called inside this method are called, I need those members.

    private IClientMqtt iClient;
    private IConnectOpts iConnectOpts;
    private MqttClient client;
    private MqttConnectOptions connOpts;
    private String clientId;
    private String url;
    private String topic;
    Logger logger;
    private ILogProvider logProvider;

@Override
    public void init() {

        Injector injector = Guice.createInjector(new GuiceModule());
        logProvider = injector.getInstance(ILogProvider.class);
        logger = logProvider.getLogger();

        iClient = injector.getInstance(IClientMqtt.class);
        try {
            client = iClient.createClient(this.url, this.clientId);

            iConnectOpts = injector.getInstance(IConnectOpts.class);
            connOpts = iConnectOpts.createConnectOpts();

            connOpts.setCleanSession(true);
            try {
                client.setCallback(this);
                client.connect();
                client.subscribe(topic);
            } catch (MqttSecurityException e) {
                logger.error("Mqtt Security Exception occurred while initializing the MQTTAppender");
            } catch (MqttException e) {
                logger.error("MqttException occurred while initializing the MQTT Appender");
            }
        } catch (ServiceSDKException ex) {
            logger.error("Service SDK Exception caused when creating MqttClient for initializing of Mqtt Appender",ex);
        }

    }

I tried with Whitebox.setInternal state by passing a mock object.

Whitebox.setInternalState(mqttAppenderSpy, "iClient",mockMqttClient);

But I am getting the below exception.

java.lang.RuntimeException: Unable to set internal state on a private field. Please report to mockito mailing list.

    at org.mockito.internal.util.reflection.Whitebox.setInternalState(Whitebox.java:29)
    at com.kohls.kube.service.sdk.logging.MQTTAppenderTest.should_create_a_mqtt_client_instance_when_init_method_is_called(MQTTAppenderTest.java:33)
    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:483)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:234)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:74)
    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:483)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
Caused by: java.lang.IllegalArgumentException: Can not set org.eclipse.paho.client.mqttv3.MqttClient field com.kohls.kube.service.sdk.logging.appender.mqtt.MQTTAppender.client to com.kohls.kube.service.sdk.logging.appender.mqtt.messaging.IClientMqtt$$EnhancerByMockitoWithCGLIB$$24b26828
    at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:167)
    at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:171)
    at sun.reflect.UnsafeObjectFieldAccessorImpl.set(UnsafeObjectFieldAccessorImpl.java:81)
    at java.lang.reflect.Field.set(Field.java:758)
    at org.mockito.internal.util.reflection.Whitebox.setInternalState(Whitebox.java:27)
    ... 28 more

Below is the test I tried to write.

@Test
    public void should_create_a_mqtt_client_instance_when_init_method_is_called() throws ServiceSDKException {
        doCallRealMethod().when(mqttAppenderSpy).init();
        IClientMqtt clientMqttMock = mock(IClientMqtt.class);
        MqttClient mockMqttClient = mock(MqttClient.class);
        when(clientMqttMock.createClient(any(String.class),any(String.class)));
        Whitebox.setInternalState(mqttAppenderSpy, "client", clientMqttMock);
        Whitebox.setInternalState(mqttAppenderSpy, "iClient",mockMqttClient);
        mqttAppenderSpy.init();
        IClientMqtt clientMqtt = (IClientMqtt) Whitebox.getInternalState(mqttAppenderSpy, "iclient");
        verify(clientMqtt.createClient(any(String.class), any(String.class)));
    }

Am I doing something wrong here? Is this the only way to test this method is there a better way I could do this? Please advice.

Aucun commentaire:

Enregistrer un commentaire