Mock private static final field using mockito or Jmockit

88,614

Solution 1

One way is using reflection get rid of final modifier from the field and then replace the LOGGER field with Mocked one

public class Class1Test {
    @Test
    public void test() throws Exception {
        Logger logger = Mockito.mock(Logger.class);
        Mockito.when(logger.isInfoEnabled()).thenReturn(false);
        setFinalStatic(Class1.class.getDeclaredField("LOGGER"), logger);
        Class1 cls1 = new Class1();
        assertFalse(cls1.demoMethod());
    }

    static void setFinalStatic(Field field, Object newValue) throws Exception {
        field.setAccessible(true);        
        Field modifiersField = Field.class.getDeclaredField("modifiers");
        modifiersField.setAccessible(true);
        modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
        field.set(null, newValue);
    }
}

Solution 2

The accepted solution shouldn't work with JDK 12. The reason can be seen here.

It is easy to do it using PowerMockito (tested with version 2.0.9). You can use the Whitebox.setInternalState method to do it for you.

Example:

Whitebox.setInternalState(MyTestClass.class, "myCar", carMock);

MyTestClass is the class containing the field.

myCar is the variable name of the field.

carMock is some mock you want to pass.

Share:
88,614
RaT
Author by

RaT

Updated on November 08, 2020

Comments

  • RaT
    RaT over 3 years

    I am using private static final LOGGER field in my class and I want LOGGER.isInfoEnabled() method to return false. How can I mock the static final field by using mockito or jMockit

    My class is:

      import org.slf4j.Logger;
      import org.slf4j.LoggerFactory;
    
      public class Class1 {
    
      private static final Logger LOGGER = LoggerFactory.getLogger(Class1.class);
    
        public boolean demoMethod() {
           System.out.println("Demo started");
           if (LOGGER.isInfoEnabled()) {
             System.out.println("@@@@@@@@@@@@@@ ------- info is enabled");
           } else {
             System.out.println("info is disabled");
           }
           return LOGGER.isInfoEnabled();
        }
      }
    

    and its junit is :

    import mockit.Mocked;
    import mockit.NonStrictExpectations;
    import org.mockito.InjectMocks;
    import org.mockito.MockitoAnnotations;
    import org.slf4j.Logger;
    import org.testng.annotations.BeforeMethod;
    import org.testng.annotations.Test;
    
    import static org.testng.Assert.*;
    import com.source.Class1;
    
    public class MyTest {
    
      @InjectMocks
      Class1 cls1;
    
      @BeforeMethod
      public void initMocks() {
        MockitoAnnotations.initMocks(this);
      }
    
      @Test
      public void test(@Mocked final Logger LOGGER) {
    
        new NonStrictExpectations() {
          {
            LOGGER.isInfoEnabled();
            result = false;
          }
        };
        assertFalse(cls1.demoMethod());
      }
    }
    

    when I run it the result is:

    -------------------------------------------------------
     T E S T S
    -------------------------------------------------------
    Running com.test.MyTest
    Configuring TestNG with: TestNG652Configurator
    Demo started
    @@@@@@@@@@@@@@ ------- info is enabled
    Tests run: 1, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 1.9 sec <<< FAILURE! - in com.test.MyTest
    test(com.test.MyTest)  Time elapsed: 0.168 sec  <<< FAILURE!
    java.lang.AssertionError: expected [false] but found [true]
            at com.test.MyTest.test(MyTest.java:35)
    
    
    Results :
    
    Failed tests:
      MyTest.test:35 expected [false] but found [true]
    
    Tests run: 1, Failures: 1, Errors: 0, Skipped: 0
    
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD FAILURE
    [INFO] ------------------------------------------------------------------------
    [INFO] Total time: 9.899s
    [INFO] Finished at: Mon Jun 08 12:35:36 IST 2015
    [INFO] Final Memory: 16M/166M
    [INFO] ------------------------------------------------------------------------
    [ERROR] Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:2.18.1:test (default-test) on project JMockDemo: The
    re are test failures.
    [ERROR]
    [ERROR] Please refer to D:\perfoce_code\workspace_kepler\JMockDemo\target\surefire-reports for the individual test results.
    [ERROR] -> [Help 1]
    [ERROR]
    [ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
    [ERROR] Re-run Maven using the -X switch to enable full debug logging.
    [ERROR]
    [ERROR] For more information about the errors and possible solutions, please read the following articles:
    [ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException
    

    I am new to jmockit and I want my above junit case to run successfully. And I have to use JMockit or mockito, can't use Powermockito. Please help.

  • RaT
    RaT almost 9 years
    Wow great, this is the solution I was looking for, thank you very much :-)
  • RaT
    RaT almost 9 years
    Why did you make setFinalStatic method static, is there any specific reason?
  • Daniel Marcotte
    Daniel Marcotte over 8 years
    This is very dangerous since it alters the static instance for your whole ClassLoader. Any subsequent test/class will have that mocked instance of the logger. This should also not be used when running tests in parallel. This can lead to serious multi-threading problems in your tests, be cautious about that.
  • Junchen Liu
    Junchen Liu over 8 years
    stupid mocking frameworks, if I want to mock it, then it should automatically change the modifier for me.
  • Joao Esperancinha
    Joao Esperancinha almost 8 years
    You can't override final methods.. You can only do that if you use reflection.
  • Dr4gon
    Dr4gon over 7 years
    @DanielMarcotte: I agree with you. Tampering with Reflection is in my opinion a last resort (i.e. in some cases it is necessary) and to be taken with a dose of caution. I rather prefer using a Framework such as Powermock for that.
  • Dr4gon
    Dr4gon over 7 years
    @JunchenLiu: Powermock does exactly that. I assume that's the reason why Mockito is not bothering with that problem rather than refering to Powermock instead.
  • Ilker Cat
    Ilker Cat almost 6 years
    @Dr4gon Can you post an example of using PowerMock for final static fields (NOT methods). I could only find examples for power-mocking methods rather than fields.
  • Ilker Cat
    Ilker Cat almost 6 years
    @DanielMarcotte: I don't agree with you since getDeclaredField contains the accessible flag and therefore it has to return a new instance each time it is called. Have a look at this nice article: artima.com/weblogs/viewpost.jsp?thread=164042. So it is not necessary to reset the flag.
  • Dr4gon
    Dr4gon almost 6 years
    @IlkerCat I quickly searched and haven't found an example in one of my project (it's been quite a long time). If I remember correctly that should work: stackoverflow.com/questions/5385161/… :D
  • Sasha
    Sasha over 3 years
    This worked for me, and it is nice and simple. I didn't have to remove the final modifer from the logger
  • Achim Krauß
    Achim Krauß almost 3 years
    getting An illegal reflective access operation has occurred to field java.lang.reflect.Field.modifiers
  • Andrew
    Andrew almost 3 years
    PowerMockito Whitebox 2.0.9 throws exception on private static final: Caused by: java.lang.IllegalAccessException: Can not set static final bla-bla field bla-bla. So reflection is only solution imho
  • zygimantus
    zygimantus over 2 years
    This does not work if field is only final, not static as well.
  • Konstantin Grigorov
    Konstantin Grigorov over 2 years
    As per the original post, "This solution will not work with latest JDK-11 or higher. As modifying the value of a final variable has been discouraged in the vanilla version of JDK-11 or +" stackoverflow.com/questions/9697531/…