Search code examples
javareflection

Attempt to change static non final field throws IllegalArgument Exception


Class that has private static non final field

package sample;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class App {
   private static Logger logger = LoggerFactory.getLogger(App.class);

   public void doSomething() {
      logger.error("Cannot do something");
   }
}

Attempt to set this field through reflection

package sample;

import org.apache.logging.log4j.core.Logger;
import org.junit.jupiter.api.Test;
import org.springframework.test.util.ReflectionTestUtils;

import static org.mockito.Mockito.mock;

public class AppTest {

    @Test
    public void doSomething() throws Exception {
        App app = new App();

        Logger mockedLogger = mock(Logger.class);

        ReflectionTestUtils.setField(App.class, "logger", mockedLogger);
    }
}

results in IllegalArgumentException

Can not set static org.slf4j.Logger field sample.App.logger to org.apache.logging.log4j.core.Logger$MockitoMock$1185285221
java.lang.IllegalArgumentException: Can not set static org.slf4j.Logger field sample.App.logger to org.apache.logging.log4j.core.Logger$MockitoMock$1185285221
    at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:167)
    at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:171)
    at sun.reflect.UnsafeStaticObjectFieldAccessorImpl.set(UnsafeStaticObjectFieldAccessorImpl.java:79)
    at java.lang.reflect.Field.set(Field.java:764)
    at org.springframework.util.ReflectionUtils.setField(ReflectionUtils.java:633)

According to JavaDocs of invoked method

https://docs.spring.io/spring-framework/docs/5.2.20.RELEASE/javadoc-api/org/springframework/test/util/ReflectionTestUtils.html#setField-java.lang.Class-java.lang.String-java.lang.Object-

Set the static field with the given name on the provided targetClass to the supplied value.

setting static field is supported.


Solution

  • Since the IllegalArgumentException is thrown by java.lang.reflect.Field.set according to the stack trace, the javadoc for Field.set can be consulted as to the reason.

    It states:

    IllegalArgumentException - if the specified object is not an instance of the class or interface declaring the underlying field (or a subclass or implementor thereof), or if an unwrapping conversion fails.

    Comparing your provided App and AppTest classes reveals that AppTest imports org.apache.logging.log4j.core.Logger whereas App imports org.slf4j.Logger

    Since you are trying to inject a mock of the logger, change the first import in AppTest to org.slf4j.logger.