在使用PowerMocktio的Java中,当类的构造函数';是否在私有嵌套类中实例化?

在使用PowerMocktio的Java中,当类的构造函数';是否在私有嵌套类中实例化?,java,unit-testing,junit,mockito,powermock,Java,Unit Testing,Junit,Mockito,Powermock,我有一个类,其中包含几个我想测试的方法,还有一个私有嵌套类,在其中一些方法中使用。在这个私有嵌套类中,它创建了一个试图与网站或数据库建立连接的对象。我想把连接到这个外部资源的测试和处理检索到的信息的测试分开。通过这种方式,我们可以选择不将测试环境连接到功能“外部”源,从而大大简化这些测试所需的设置 为此,我正在编写一个测试,模拟试图形成这些连接的对象的构造函数。当嵌套的私有类试图形成连接时,我不希望它做任何事情,当它试图检索信息时,我希望它只返回预定义的数据字符串。目前,我有一些类似的东西: p

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

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

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需要在注释中包含一个常量,这根本不起作用

有人能想出一种方法让这个模拟构造函数工作吗


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

mockConnector=mock(Connection.class)


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

我不确定您是否正在运行与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);
    }
}

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

  • 编写一个涵盖此代码的大型集成测试,这样您就有了一个安全网,以防您的更改可能会破坏某些东西。此测试可能会创建内存中的数据库/后端,并将连接器连接到该数据库/后端
  • 进行小而安全的更改,为测试创建接缝
  • 两者都做比较好。有关更多详细信息,请参阅本书

    我将展示选项2的一个示例

    首先,您可以创建一个“接缝”,使测试能够更改
    连接器的创建方式:

    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的测试可能很脆弱,并且容易被破坏,只需对测试中的代码进行一些小的更改。请注意,使用部分模拟也可能很脆弱。我们很快就会解决的

    测试通过后,可以重构代码,以便调用者通过
    连接器
    工厂:

    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。如果这不是一个选项,您可以创建第二个无参数构造函数,该构造函数传入一个real
    ConnectorFactory

    假设测试仍然通过,您可以将测试更改为mock
    ConnectorFactory
    ,而不是对
    MyClass
    进行部分模拟,从而降低测试的脆弱性。当这些测试通过时,您可以内联
    createConnector()


    以后,试着在编写代码时(或者至少在花很多时间在手动测试之前)编写测试。也就是说,可以是模拟数据库,也可以是模拟http服务器,这取决于所需的连接类型。@Seb其中一个答案解决了您的问题吗?如果是,请接受。如果没有,您可以编辑您的问题并添加有关问题的更多详细信息problem@NamshubWriter恐怕没有一个答案能解决这个问题。我也不知道我还能补充什么来让问题更清楚。答案似乎没有仔细阅读我的问题,或者建议我编辑我正在测试的代码并避免所有问题,这是我无法做到的,并且已经作为注释添加到了问题中。@Seb是您无法修改代码的唯一原因,因为它目前可以工作?如果是这样,并且如果您无法找到其他方法来测试代码,那么您的选择似乎是在不进行单元测试的情况下使用代码,还是对代码进行安全更改?我不确定您的意思。我还没有为这个示例编写Connection类的功能,因为在我试图模拟它时,这并不重要。创建模拟对象不是我的问题。当私有嵌套类为此类类型调用构造函数时,它将返回模拟对象……您不必使用注释。如果你这样做是很好的,但你不受它的约束。