Mocking a Private Variable that is Assumed to Exist

前端 未结 2 2008
日久生厌
日久生厌 2021-02-12 10:25

How can you get a mock object in at runtime when it is not created/initialized in the class you are testing, it is not static (singleton pattern), or you don\'t have some sort o

相关标签:
2条回答
  • 2021-02-12 11:22

    Several approaches here:

    1. ReflectionTestUtils of Spring Testing framework: ReflectionTestUtils.setField(objectToTest, "privateFieldName", mockObjectToInject);. With this you don't introduce another dependency.
    2. org.mockito.internal.util.reflection.FieldSetter.
    3. PowerMock.Whitebox.setInternalState() to mock a private field.

    If you need to mock internal local variable creation, use PowerMockito.whenNew(Foo.class).withNoArguments().thenReturn(foo);. Very, very useful. Cannot find other ways to do the same.

    With only Mockito you cannot mock local variable creation, because when(any(Foo.class) does not work; will return null. It compiles but does not work.

    References: Mockito: Mock private field initialization

    0 讨论(0)
  • 2021-02-12 11:25

    After a lot more hunting around and looking at all the options Mockito/Powermock had to offer, I found the solution (which I will share in case others run into this same issue).

    When you have private member variables that are never initialized (and just assumed created in other places), you can use the @InjectMocks annotation to "inject" Mocks you want into your class you are testing.

    1. Add a variable in your test class for the class you are testing, and give it the annotation @InjectMocks (org.Mockito.InjectMocks).
    2. Use @Mock annotations to setup the mocks you want to inject. Use the @Mock (name = "privateVariableNameHere") name property to map the Mock object to the private variable inside your class you are testing.
    3. In either a setup function or before you call your class, initialize the mocks. The easiest way I have found is to use a "setup" method with the @Before annotation. Then inside there call MockitoAnnotations.initMocks(this); to quickly initialize anything with the @Mock annotation.
    4. Define your Mock functionality in your test method (before calling the method you are testing).
    5. Using the @InjectMock object, call your method you are testing... the mocks SHOULD be hooked in and working as defined in the earlier steps.

    So for the example class I use above, the code to test/mock would have Connection returned as a mock which you can do whatever with. Based on the example above in my question, this is what the code would look like:

    @RunWith(PowerMockRunner.class)
    @PrepareForTest({/* Static Classes I am Mocking */})
    public class ExampleTest {
      @Mock (name = "queueFactory") //same name as private var.
      QueueConnectionFactory queueFactoryMock;
      @Mock
      Connection connectionMock; //the object we want returned
      @InjectMocks
      Example exampleTester; //the class to test
    
      @Before
      public void setup(){
        MockitoAnnotations.initMocks(this); // initialize all the @Mock objects
        // Setup other Static Mocks
      }
    
      @Test
      public void testTestMe(){
        //Mock your objects like other "normally" mocked objects
        PowerMockito.when(queueFactoryMock.createConnection()).thenReturn(connectionMock);
        //...Mock ConnectionMock functionality...
        exampleTester.testMe();
      }
    }
    
    0 讨论(0)
提交回复
热议问题