Mockito/PowerMock:如何在 SUT 中重置模拟静态变量?

发布于 2024-11-03 11:42:42 字数 1782 浏览 5 评论 0原文

我讨厌将单元测试引入遗留代码库,但我必须这样做。
到目前为止,我使用 Mockito 和 PowerMock 成功地将单元测试引入到遗留代码库中。工作得很好,直到我遇到这种情况:

在SUT中,有几个静态变量(我在PowerMock的帮助下模拟了它们,模拟静态方法和模拟构造函数)。
现在在第一个测试方法中,一切正常,并且模拟的静态变量返回了预期的输出值。
但是在后续的测试方法中,模拟的静态对象总是返回在第一个测试中设置的值,尽管我在测试之前确实对其调用了reset()。

// 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
  }
}



由于 SUT 的构造函数仅在静态 c1 为 null 时实例化 c1 和 c2,因此它们 (c1, c2) 不会在子序列调用中重新实例化。我不明白的是为什么reset(c1),reset(c2)在test2中没有效果?

有什么想法吗?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

ゝ偶尔ゞ 2024-11-10 11:42:42

终于可以工作了基本上,我无法在两个不同的测试运行中设置存根(模拟的静态实例变量)。我必须在第一个@Before 中设置预期的行为。
而不是使用

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

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

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

因此,我应该使用以下序列

@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
  }

: PowerMock/Mockito 中的一些限制(错误?)?

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?

樱娆 2024-11-10 11:42:42

如果您使用 Mockito 来模拟静态方法,则需要

  1. 创建静态模拟在 try 范围内 (try (MockedStaticutilities = Mockito.mockStatic(StaticUtils.class)) {)
  2. 如果您在 try 范围之外创建了静态模拟,关闭 测试后静态模拟(<代码>utilities.close())

If you are using Mockito to mock static methods, you need to either

  1. Create static mock in the try scope (try (MockedStatic<StaticUtils> utilities = Mockito.mockStatic(StaticUtils.class)) {)
  2. If you have created the static mock outside try scope, close the static mock after the test (utilities.close())
堇年纸鸢 2024-11-10 11:42:42

尝试将静态模拟设置移动到 @BeforeClass 设置方法,但将 Reset(mocks) 调用保留在测试 setup() 方法中。您只想设置您的mockStatic一次,但因为它们是静态的,所以您需要为每个测试重置它们,否则它们会与后续测试混淆。

即尝试

@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
}

...

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
}

...

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文