【问题标题】:Java - Mock private static constructor for multiple testsJava - 用于多个测试的模拟私有静态构造函数
【发布时间】:2020-04-01 23:25:41
【问题描述】:

我是 java 测试的新手,现在一直在摆弄这个,没有运气,我有以下课程:

public class Bar {
    public Object doSomething(int a, String b){
        return "something";
    }

    public Object doSomethingElse(int a, int b, String c){
        return "something else";
    }
}
public class Foo {
    private static Bar bar = new Bar();

    public static void start(int a, int b, String c){
        if(a == 1) { // some calculated condition
            bar.doSomething(a, c);
        } else {
            bar.doSomethingElse(a, b, c);
        }
    }
}
@RunWith(PowerMockRunner.class)
@PrepareForTest(Foo.class)
public class FooTest {
    @Test
    public void somethingTest() throws Exception {
        Bar barMock = createMock(Bar.class);

        expectNew(Bar.class).andReturn(barMock);

        expect(barMock.doSomething(1, "xxx")).andReturn("ABC");

        replay(barMock, Bar.class);

        Foo.start(1, 2, "xxx");
        verify(barMock, Bar.class);
    }

    @Test
    public void somethingElseTest() throws Exception {
        Bar barMock = createMock(Bar.class);

        expectNew(Bar.class).andReturn(barMock);

        expect(barMock.doSomethingElse(0, 2,"xxx")).andReturn("ABC");

        replay(barMock, Bar.class);

        Foo.start(0, 2, "xxx");
        verify(barMock, Bar.class);
    }
}

单独运行测试,但不是整个班级,我认为这与:

 private static Bar bar = new Bar();

但我不能 100% 确定。无论哪种方式,假设我无法更改 foo / bar 类,我该如何解决这个问题?

【问题讨论】:

  • 私有静态构造函数?
  • 我意识到这不是很好,我只是想弄清楚如何测试这样的东西。

标签: java junit mockito powermockito easymock


【解决方案1】:

我会将 Foo 重构为:

public class Foo {
    private static Bar bar = new Bar();

    public static void start(int a, int b, String c){
        if(a == 1) { // some calculated condition
            doSomething(a, c);
        } else {
            doSomethingElse(a, b, c);
        }
    }

    private static void doSomething(int a, String c) {
        bar.doSomething(a, c);
    }

    private static void doSomethingElse(int a, int b, String c) {
        bar.doSomethingElse(a, b, c);
    }
}

然后像这样测试 Foo:

import static org.mockito.Matchers.eq;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.AdditionalMatchers;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;


@RunWith(PowerMockRunner.class)
@PrepareForTest({Foo.class})
public class FooTest {

    @Before
    public void setup() throws Exception {
        PowerMockito.spy(Foo.class);
    }


    @Test
    public void somethingTest() throws Exception {
        Foo.start(1, 2, "xxx");   
        PowerMockito.verifyPrivate(Foo.class).invoke("doSomething", eq(1), eq("xxx"));
    }

    @Test
    public void somethingElseTest() throws Exception {
        Foo.start(0, 2, "xxx"); 
        PowerMockito.verifyPrivate(Foo.class).invoke("doSomethingElse", AdditionalMatchers.not(eq(1)), eq(2), eq("xxx"));
    }
}

并单独测试 Bar,可以简单地使用 Mockito 完成。

【讨论】:

    【解决方案2】:

    您面临的问题是classstatic 字段仅初始化一次。因此,第一个测试创建了一个新的 Bar 对象,但是这个模拟在所有其他测试中都存在,因为没有触发新的创建。

    相反,您应该使用Reflections 为每个测试设置private static 字段。 PowerMock 为您提供了一个名为 Whitebox 的实用程序类。

    还请注意,setInternalState 仅在该类中恰好有一个 Bar 对象的实例时才有效。如果有多个,您将需要依赖普通的 Java 反射类。

    示例见下:

    @RunWith(PowerMockRunner.class)
    @SuppressStaticInitializationFor("test.Foo") // optional, only required if the Bar class could not be created normally.
    public class StaticTest {
    
        @Test
        public void somethingTest() throws Exception {
            Bar barMock = createMock(Bar.class);
            Whitebox.setInternalState(Foo.class, barMock);
    
            expect(barMock.doSomething(1, "xxx")).andReturn("ABC");
    
            replay(barMock, Bar.class);
    
            Foo.start(1, 2, "xxx");
            verify(barMock, Bar.class);
        }
    
        @Test
        public void somethingElseTest() throws Exception {
            Bar barMock = createMock(Bar.class);
            Whitebox.setInternalState(Foo.class, barMock);
    
            expect(barMock.doSomethingElse(0, 2,"xxx")).andReturn("ABC");
    
            replay(barMock, Bar.class);
    
            Foo.start(0, 2, "xxx");
            verify(barMock, Bar.class);
        }
    }
    

    我将此与静态初始化的抑制结合起来,但严格来说,这对您的测试来说不是必需的,因为 Bar 类的创建无论如何都会成功。

    注释应该只在Bar类不能被创建的情况下使用。 请注意,您需要在此处输入包括包名称在内的完全限定名称(我假设包为test)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-01
      • 2012-10-26
      • 1970-01-01
      相关资源
      最近更新 更多