繁体   English   中英

在带有PowerMocktio的Java中,如何在私有嵌套类中实例化该类的构造函数?

[英]In Java with PowerMocktio, how do you mock the constructor of a class when it's instantiated inside a private nested class?

我有一个包含要测试的几种方法的类,以及在其中一些方法中使用的私有嵌套类。 在这个私有的嵌套类中,它创建一个对象,该对象试图与网站或数据库建立连接。 我想分开测试,以连接到该外部资源以及对检索到的信息进行的处理。 这样,我们可以选择不将测试环境连接到功能“外部”源,从而大大简化了这些测试所需的设置。

为此,我正在编写一个测试,该测试模拟试图建立这些连接的对象的构造函数。 当嵌套的私有类尝试形成连接时,并且当它尝试检索信息时,我不希望它做任何事情,我希望它仅返回预定义的数据字符串。 目前,我有一些类似的东西:

public class MyClass {

    public int mainMethod() {
        //Some instructions...

        NestedClass nestedClass = new NestedClass();
        int finalResult = nestedClass.collectAndRefineData();
    }

    private class NestedClass {

        public NestedClass() {
            Connector connect = new Connector();
        }

        public int collectAndRefineData() {
            //Connects to the outside resource, like a website or database

            //Processes and refines data into a state I want

            //Returns data
        }
}

测试类如下所示:

@RunWith(PowerMockRunner.class)
@PrepareForTest({Connector.class})
public class MyClassTests {

    @Test
    public void testOne() {
        mockConnector = mock(Connection.class);
        PowerMockito.whenNew(Connector.class).withNoArguments().thenReturn(mockConnector);

        MyClass testClass = new MyClass();
        int result = testClass.mainMethod();

        Assert.equals(result, 1);
    }
}

现在,我确实知道 ,在PrepareForTest批注中,我需要包括实例化要为其模拟构造函数的对象的类。 问题是我不能放MyClass,因为那不是创建它的对象,我不能放NestedClass,因为它在测试中看不到。 我曾尝试将MyClass.class.getDeclaredClasses [1]放入正确的类中,但是不幸的是PowerMocktio需要在注释中添加一个常量,这根本行不通。

谁能想到一种使此模拟构造函数起作用的方法?


注意:我无法对正在测试的代码进行任何更改。 这是因为该代码目前可以正常工作,已经过手动测试,我正在编写此代码,以便将来的项目将使用此自动测试框架。

我不确定您是否正在运行与Mockito集成的测试,如果可以,则可以使用以下代码:

@RunWith(PowerMockRunner.class)
@PrepareForTest({Connector.class})
public class MyClassTests {

    @Mock
    Connector mockConnector;

    @InjectMocks
    MyClass testClass;

    @Test
    public void testOne() {
        PowerMockito.whenNew(Connector.class).withNoArguments().thenReturn(mockConnector);

        int result = testClass.mainMethod();

        Assert.equals(result, 1);
    }
}

我怀疑您将不得不修改正在测试的代码。 您有两种选择:

  1. 编写涵盖此代码的大型集成测试,以便您拥有一个安全网 ,以防万一您的更改可能破坏某些东西。 该测试可能会创建一个内存数据库/后端,并使连接器连接到该数据库/后端。
  2. 进行小而安全的更改 ,以创建测试接缝

最好同时做两个。 有关更多详细信息,请参见《 有效处理旧版代码 》一书。

我将显示一个选项2的示例。

首先,您可以创建一个“ seam”,以使测试能够更改Connector的创建方式:

public class MyClass {

    public int mainMethod() {
        // Some instructions...

        NestedClass nestedClass = new NestedClass();
        return nestedClass.CollectAndRefineData();
    }

    // visible for testing
    Connector createConnector() {
        return new Connector();
    }

    private class NestedClass {
        private final Connector connector;

        public NestedClass() {
            connector = createConnector();
        }

        ...
    }
}

然后,您可以使用MyClass部分模拟来测试您的代码。

@RunWith(JUnit4.class)
public class MyClassTests {

    @Test
    public void testOne() {
        MyClass testClass = spy(MyClass.class);
        Connector mockConnector = mock(Connector.class);
        when(testClass.createConnection())
            .thenReturn(mockConnector);

        int result = testClass.mainMethod();

        Assert.assertEquals(1, result);
    }
}

请注意, assertEquals期望第一个参数为期望值。

另请注意,此测试使用Mockito而不是PowerMock 这样做很好,因为使用PowerMock的测试可能会很脆弱,并且由于被测代码的微小更改而容易损坏。 请注意,使用部分模拟也可能很脆弱。 我们会尽快解决。

通过测试后,您可以重构代码,以便调用方通过Connector工厂:

public class MyClass {
    private final ConnectorFactory connectorFactory;

    @Inject
    MyClass(ConnectorFactory factory) {
        this.connectorFactory = factory;
    }

    // visible for testing
    Connector createConnector() {
        return connectorFactory.create();
    }

    private class NestedClass {
        private final Connector connector;

        public NestedClass() {
            connector = createConnector();
        }

        ...
    }
}

使用MyClass的代码最好使用依赖注入框架,例如Guice或Spring。 如果这不是一个选择,则可以使第二个无参数构造函数传入真实的ConnectorFactory

假设测试仍然通过,则可以通过将测试更改为模拟ConnectorFactory而不是对MyClass进行部分模拟来使测试不那么脆弱 当这些测试通过时,可以内联createConnector()

将来,请尝试在编写代码时编写测试 (或至少在花费大量时间进行手动测试之前)。

mockConnector =模拟(Connection.class);

未声明此对象。 无论如何,无论如何,您都必须使用@Mock注释。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM