【问题标题】:Null pointer on an autowired bean which is not mocked by mockito自动装配 bean 上的空指针,不被 mockito 模拟
【发布时间】:2019-02-26 06:40:53
【问题描述】:

我有一个需要进行单元测试的服务类。该服务有一个上传方法,该方法依次调用更新数据库的其他服务(自动装配的 bean)。我需要模拟其中一些服务和一些按原样执行。

@Service
public class UploadServiceImpl implements UploadService{
  @Autowired
  private ServiceA serviceA;

  @Autowired
  private ServiceB serviceB;

  public void upload(){
    serviceA.execute();
    serviceB.execute():

    //code...
}

在上面的示例中,我需要模拟 ServiceA,但我希望 ServiceB 按原样运行并执行其功能。 我的 Junit 测试如下所示:

@RunWith(SpringJUnit4ClassRunner.class) 
@SpringBootTest(classes=Swagger2SpringBoot.class) 
public class UploadServiceTest {
  @Mock
  private ServiceA serviceA;

  @InjectMocks
  private UploadServiceImpl uploadService;

  @Before
  public void init() {
    MockitoAnnotations.initMocks(this);
  }

  @Test
  public void testUpload(){
    uploadService.upload();

  }

当我执行此操作时,我会在 serviceB.execute(); in UploadServiceImpl 获得 NPE。

可能是什么问题?

注意:我没有指定模拟对象的行为,因为我并不关心,而且模拟对象的默认行为是什么都不做。

谢谢!

【问题讨论】:

  • 一起试试@InjectMocks@Autowired
  • 更多更干净,使用构造函数注入,并且您根本不需要 Spring 进行此测试。
  • 嗯,既然你通过了serviceA.execute();,那么也许你也应该@Mock B(你在测试中嘲笑A。)
  • @rustyx 使用 @InjectMocks@Autowired 注释服务工作!

标签: java spring-boot mockito


【解决方案1】:

添加

  @Mock
  private ServiceB serviceB;

创建缺失服务的可注入模拟,就像您对服务 A 所做的那样。

【讨论】:

    【解决方案2】:

    通常在单元测试时你想模拟一个类的所有外部依赖。这样,单元测试可以保持独立并专注于被测类。

    不过,如果您想将 Spring 自动装配与 Mockito 模拟混合使用,一个简单的解决方案是同时使用 @InjectMocks@Autowired 进行注释:

      @InjectMocks
      @Autowired
      private UploadServiceImpl uploadService;
    

    这样做的最终结果是,首先 Spring 将自动装配 bean,然后 Mockito 将立即用可用的模拟覆盖模拟的依赖项。

    【讨论】:

      【解决方案3】:

      您面临的问题是由于使用了@InjectMocks 注释。@InjectMocks 标记了应该执行注入的字段。 Mockito 将尝试仅通过构造函数注入、setter 注入或属性注入来注入模拟——按此顺序。如果任何给定的注入策略失败,则 Mockito 不会报告失败。

      因此,在您尝试注入模拟时,只有一个模拟 bean 存在,而另一个 bean ServiceA 没有被注入。要解决这个问题:

      您可以尝试完全不使用@InjectMocks,而是为您要模拟的方法传递一个模拟对象,同时将其余的自动装配对象传递给构造函数。示例:

      在这里进行测试,我传递了一个模拟对象和一个自动装配的对象。

      @RunWith(MockitoJUnitRunner.class)
      public class SampleTestServiceImplTest {
      
      @Mock
      private SampleClient sampleClient;
      @Autowired
      private BackendService backendService ;
      
      private BackendServiceImpl backendServiceimpl;
      
      @Before
      void setUp() {
          backendServiceimpl = new BackendServiceImpl(sampleClient, backendService);
      }
      

      或者另一种方法是使用@Autowired 注释和@InjectMocks.@Autowired @InjectMocks 一起使用,它的作用是注入模拟类,Autowired 注释添加该类可能具有的任何其他依赖项。

      答案来自:https://medium.com/@vatsalsinghal/autowired-and-injectmocks-in-tandem-a424517fdd29

      【讨论】:

        【解决方案4】:

        在我的例子中,除了使用@InjectMocks 和@Autowired 的组合之外,我还必须为测试类中的模拟对象提供设置器(在原始示例中为UploadServiceImpl 中的ServiceA 设置器)。没有那个,ServiceA 的真正方法就被调用了。

        【讨论】:

          【解决方案5】:

          另一种方法是定义一个自动装配的构造函数,以便您可以正确地测试服务。

          @Service
          public class UploadServiceImpl implements UploadService{
          
            private ServiceA serviceA;
            private ServiceB serviceB;
          
            @Autowired
            public UploadServiceImpl(ServiceA serviceA, ServiceB serviceB) {
              this.serviceA = serviceA;
              this.serviceB = serviceB;
            }
          
            public void upload(){
              serviceA.execute();
              serviceB.execute():
          
              //code...
          }
          

          【讨论】:

            猜你喜欢
            • 2021-09-02
            • 2014-11-17
            • 2012-10-02
            • 2015-11-16
            • 1970-01-01
            • 2017-12-27
            • 1970-01-01
            • 2019-04-04
            • 1970-01-01
            相关资源
            最近更新 更多