Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mockito/PowerMock: how to reset a mocked static variable in SUT?

I hate to introduce Unit-tests into a legacy code base, but I have to.
Up untill now I successfully introduced unit-testing into the legacy code base using Mockito and PowerMock. Worked perfectly well until I met with this situation:

in the SUT, there're several static varibles (which I mocked with the help of PowerMock, mocking static methods and mocking constructors).
Now in the first test method, all worked fine and the mocked static var returned the expected output value.
but in the subsequent test methods, the mocked static object always returns value that has been set in the first test, although I did call reset() on it before the tests.

// legacy code base:
public class SUT {
  private static Collaborator1 c1 = null;
  private static Collaborator2 c2 = null;

  public SUT(param1) {
    if (c1 == null) {
        c1 = Collaborator1.instance(param1);
        c2 = new Collaborator2(c1);
    } else {
    }
  }
}



// newly introduced unit tests:
@RunWith(PowerMockRunner.class)
@PrepareForTest({
    SUT.class,                  // to mock: new Collaborator2(..), as required by PowerMock when mocking constructors
    Collaborator1.class,        // to mock: Collaborator1.instance(..), as required by PowerMock in mocking static methods
})
public class SUTTest {

  private SUT sut;

  private Collaborator1 c1 = mock(Collaborator1.class);
  private Collaborator2 c2 = mock(Collaborator2.class);

  @Before  
  public void setup() {
    // mock c1:
    PowerMockito.mockStatic(Collaborator1.class);
    when(Collaborator1.instance(param1)).thenReturn(c1);

    // mock c2:
    PowerMockito.whenNew(Collaborator2.class).withArguments(c1).thenReturn(c2);

    reset(c1);
    reset(c2);

    sut = new SUT(param1);
  }

  @Test
  public void test1() {
    when(c2.foo(input1)).thenReturn(out1); 

    // do something
  }

  @Test
  public void test2() {
    when(c2.foo(input2)).thenReturn(out2);    // BANG!!! c2.foo(input2) always return "out1"

    // do something
  }
}



Since the constructor of SUT only instantiates c1 and c2 if the static c1 is null, they (c1, c2) don't get re-instantiated in sub-sequence calls. What I don't understand is why reset(c1), reset(c2) have no effect in test2?

Any idea?

like image 458
Tumer Avatar asked Apr 26 '11 06:04

Tumer


People also ask

Can we set values in mocked object?

You can set the mock object property to the value returned by the mock object method. To achieve this, specify separate behaviors for the method and the property. You can specify one behavior at a time.

What does Mockito Reset do?

Mockito provides the capability to a reset a mock so that it can be reused later.

Can we mock static methods in Mockito?

Mockito allows us to create mock objects. Since static method belongs to the class, there is no way in Mockito to mock static methods. However, we can use PowerMock along with Mockito framework to mock static methods.


2 Answers

Got it work finally. Basically, I can't set the stub (the mocked static instance variable) in two different test runs. I have to setup the expected behavior in the first @Before.
So instead of using

  @Before  
  public void setup() {
    ...
  }

  @Test
  public void test1() {
    when(c2.foo(input1)).thenReturn(out1); 
  }

  @Test
  public void test2() {
    when(c2.foo(input2)).thenReturn(out2); 
  }

I should use this sequence:

@Before  
  public void setup() {
    when(c2.foo(input1)).thenReturn(out1); 
    when(c2.foo(input2)).thenReturn(out2);
  }

  @Test
  public void test1() {
    // do something
  }

  @Test
  public void test2() {
    // do something
  }

Some limitation(bug?) in PowerMock/Mockito?

like image 101
Tumer Avatar answered Oct 23 '22 05:10

Tumer


Try moving your static mock setup to an @BeforeClass setup method, but leave your reset(mocks) call in your test setup() method. You want to setup your mockStatic only once, but because they're static, you will want to reset them for every test or they'll muddle with subsequent tests.

i.e. try

@BeforeClass  
public void setupClass() {
    // mock c1:
    PowerMockito.mockStatic(Collaborator1.class);
    when(Collaborator1.instance(param1)).thenReturn(c1);

    // mock c2:
    PowerMockito.whenNew(Collaborator2.class).withArguments(c1).thenReturn(c2);
}

@Before  
public void setup() {
  reset(c1);
  reset(c2);

  sut = new SUT(param1);
}

@Test
public void test1() {
  // do something
}

...

like image 38
VinnyQ77 Avatar answered Oct 23 '22 05:10

VinnyQ77