当前位置: 首页 > 知识库问答 >
问题:

深度模拟不是@InjectMocks的成员

彭成天
2023-03-14

注释InjectMocks为我们提供了存根/填充私有成员和重用测试用例的方法。这是我们填充假成员时发生问题的概念代码。

  public class TestBuilder{
      @Spy
      private StubComponent componentA = new StubComponent();
      @Mock
      private FakeComponent componentB;
      @InjectMocks
      private class TestTarget targetInstance = mock(TestTarget.class);

      public static Class TestTarget{
        private StubComponent componentA;
        private FakeComponent componentB;
        public ShimmedResultB testInvokation(String para){
            componentA.doCallRealMethod();
            ShimmedResultA shimmedResultA = componentA.someUnableToStubbedMethod(para);
            ShimmedResultB shouldNotBeNull = componentB.someShimmedMethod(shimmedResultA);
            return shouldNotBeNull;
        }
      }

      private TestBuilder(){
        MockitoAnnotations.initMocks(this);
        //Shim the real component A with partial stubbed
        doReturn(shimmedResultA).when(componentA).someUnableToStubbedMethod(any());
        //Shim the fake component B
        //************The issue is here****************
        componentB = mock(FakeComponent.class);
        //*********************************************
        when(componentB.someShimmedMethod(any())).thenReturn(shimmedResultB);

      }
      public TestTarget getTargetInstance(){
        return this.targetInstance;
      }

      public static TestTarget build(){
        return (new TestBuilder()).getTargetInstance();
      }

      public static main(String[] args){
        TestTarget testInstance = TestBuilder.build();
        ShimmedResultB result = testInstance.testInvokation("");
        assertThat(result, not(equalTo(null)));
      }
  }

问题是当我们模拟假的组件B时。然后,someShimmedMethod将返回null。似乎InjectMock无法将mock()携带到私有成员。

以下是一些术语定义:

>

  • StubComponent:测试将作为私有成员渗透到这个组件。但是,有些方法可能无法通过。我们可以改变它的公共方法。该组件可能具有较小的依赖范围,很容易由本地资源发起。

    FakeComponent:该组件将在其他地方测试。在这里,我们只能构建模拟实例并填充测试目标将利用的所有方法。

    存根:@Spy可以帮助我们钩住存根成员。私人会员并非百分之百真实。但一些留有树桩的部分可能会让测试渗透到这个私人成员中。

    Shim:@Mock会给我们一个空指针,直到initMocks。所以我们可以在initMocks之后开始设计Fake组件的返回。这就是@InjectMocks的魔力。然而,这是最棘手的部分,因为开发人员希望直观地启动组件B的每一件事并模拟(FakeComponent.class)。这将清理所有的闪光设计并使您的断言失败。

    ==================================================================

    感谢Maciej的回答,很抱歉在我翻译测试用例的结构时出现了错字。让我用更清晰的描述来提出这个问题。

      public class TestBuilder{
          @Spy
          private StubComponent componentA = new StubComponent();
          @Mock
          private FakeComponent componentB;
          @InjectMocks
          private TestTarget targetInstance = mock(TestTarget.class);
    
          public static Class TestTarget{
            private StubComponent componentA;
            private FakeComponent componentB;
            public ShimmedResultB testInvokation(String para){
                componentA.doCallRealMethod();
                ShimmedResultA shimmedResultA = componentA.someUnableToStubbedMethod(para);
                ShimmedResultB shouldNotBeNull = componentB.someShimmedMethod(shimmedResultA);
                return shouldNotBeNull;
            }
    
            public TestTarget(){
                //The FakeComponent has some specific remote resource
                //And could not be initialized here
                componentB = new FakeComponent();
                //We will use mock server to test this FakeComponent else where
            }
          }
    
          private TestBuilder(){
            //Hook the testing Function for trigger the step in
            doCallRealMethod().when(this.targetInstance).testInvokation(anyString());
            //Inject Stubbed and Faked Private Member for testing
            MockitoAnnotations.initMocks(this);
            //Shim the real component A with partial stubbed
            doReturn(shimmedResultA).when(componentA).someUnableToStubbedMethod(any());
    
            //************The issue is here****************
            componentB = mock(FakeComponent.class);
            //*********************************************
            //Shim the leveraged method of fake componentB
            when(componentB.someShimmedMethod(any())).thenReturn(shimmedResultB);
          }
    
          public TestTarget getTargetInstance(){
            return this.targetInstance;
          }
    
          public static TestTarget build(){
            return (new TestBuilder()).getTargetInstance();
          }
    
          public static main(String[] args){
            TestTarget testInstance = TestBuilder.build();
            //The doRealCall hook will trigger the testing
            ShimmedResultB result = testInstance.testInvokation("");
            assertThat(result, not(equalTo(null)));
          }
      }
    

    第二个概念代码中添加了一些内容:

    >

  • 组件B是我们不想介入的范围。但是,TestTarget在其构造函数中初始化了组件B。当我们有一个与远程源相关的实用程序时,这很常见。我们使用mock服务器或其他技术独立测试组件B。因此,我们只能使用mock(TestTarget.class)。

    因为我们模仿了TestTarget。我遗漏了一件事,那就是我们需要使用doCallRealMethod()。当(targetInstance)触发testInvokation()时。这限制了targetInstance的空声明。我们需要mock()并钩住doCallRealMethod。

    因此,结果是我们需要将@Mock保留为空,而不使用任何mock()来让@InjectMocks处理垫片。我们刚刚发现当我们使用@InjectMocks时这很棘手。


  • 共有1个答案

    习斌
    2023-03-14

    问题在于@InjectMocks定义:

    @InjectMocks
    private class TestTarget targetInstance = mock(TestTarget.class);
    

    被测试的类永远不应该是mock(也是为什么是class关键字)。

    尝试使用:

    @InjectMocks
    private TestTarget targetInstance = new TestTarget();
    

    或者简单地说:

    @InjectMocks
    private TestTarget targetInstance;
    
     类似资料:
    • 所以我有一个类需要测试。我们把它叫做ClassToTest。它有两个Dao对象作为字段。 正如您所看到的,ClassToTest不包含任何构造函数或setter,我正在使用spring自动关联字段。 现在,我有了一个具有classToTest所需的所有依赖项的基本测试类: 并且testClass扩展了这个BaseTest类: 这将导致保存时出现空指针异常。但是,如果我将设置方法更改为: 考试通过了

    • 例如,我有处理程序: 但是当我试图模拟它时,它实际上调用。如何使用来模拟它的方法?

    • 我正在SpringMVC上使用Mockito进行JUnit测试。测试使用@InjectMock和@Mock with when(方法(…)。然后返回(X)。问题是如何在@Inject实例中@Mock方法? 我尝试过创建两个实例,例如@InjectMocks Foo foInstance和@Mock Foo foInstanceMock;我的思维方式是区分注入什么实例和嘲笑什么实例。我也尝试使用间谍

    • 我有一个间接使用类Foo的测试用例。对于测试用例,我不在乎Foo是什么。我应该可以嘲笑它。 然而,testcase使用一个库来调用Foo上的一些方法。其中一些方法返回对象,然后该库对这些返回的对象调用一些方法。就本测试而言,这些对象是什么并不重要,只是它们不是null,并且不会导致NullPointerException。 对于该库调用的每个对象和方法,我已经经历并添加了一系列类似以下的expec

    • 我正在用两个应用程序做project:android应用程序(客户端)和rest服务(服务器)。我的android应用程序消耗了我的rest服务。 这两个应用程序都是单独测试的,以确保它们按照预期完成业务。在服务器测试期间,我准备请求并检查服务器响应。在客户机测试期间,我设置了一个简单的http模拟服务器,并针对不同的模拟响应测试客户机的请求。 现在,这个技术很管用。它给了我一种我喜欢的灵活性。我

    • 我想编写一些使用JUnit4.12、Mockito1.9.5和PowerMock1.6.1的单元测试。这个类有一些用@mock注释的字段,还有一些用@injectmocks注释的字段。用@InjectMocks注释的属性在某个点到达一个父构造函数,该父构造函数包含一些静态方法调用,应该用PowerMock来模拟。问题是第一个测试是无缝工作的,而第二个测试似乎根本没有嘲弄静态方法。 如前所述,第一个