首页 文章

Spring @Autowired构造函数导致@Value在测试类中实例化时返回null

提问于
浏览
4

我在服务中使用自动装配的构造函数,当在测试类中实例化时,会导致@Value注释返回null . 自动装配依赖项直接解决了问题,但项目遵循使用基于构造函数的自动装配的惯例 . 我的理解是,在测试类中实例化服务并不是从Spring IoC容器创建它,导致@Value返回null . 有没有办法使用基于构造函数的自动装配从IoC容器创建服务,而无需直接访问应用程序上下文?

示例服务:

@Component
public class UpdateService {

   @Value("${update.success.table}")
   private String successTable;

   @Value("${update.failed.table}")
   private String failedTable;

   private UserService userService

   @Autowired
   public UpdateService(UserService userService) {
      this.userService = userService;
   }
}

示例测试服务:

@RunWith(SpringJUnite4ClassRunner.class)
@SpringApplicationConfiguration(classes = {TestApplication.class})
@WebAppConfiguration
public class UpdateServiceTest {

   private UpdateService updateService;

   @Mock
   private UserService mockUserService;

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

      updateService = new UpdateService(mockUserService);

   }
}

2 回答

  • 2

    要使 @Value 工作 updateService 应该在spring上下文中 .

    Spring框架集成测试的最佳实践是在测试环境中包含应用程序上下文,并在测试中包含自动装配测试源:

    ...
    public class UpdateServiceTest  { 
        @Autowired 
        private UpdateService updateService;
        ...
    

    Mock userService

    userService 更改为 protected 的选项,并考虑测试和源类在同一个包中 .

    @Before
    public void setUp() {
       MockitoAnnotations.initMocks(this);
    
       updateService.userService = mockUserService;
    }
    

    使用Whitebox反射的选项:

    @Before
    public void setUp() {
       MockitoAnnotations.initMocks(this);
    
       Whitebox.setInternalState(updateService, 'userService', mockUserService);
    }
    
  • 2

    @Value 由属性占位符配置器填充,该配置器是 spring 上下文中的后处理器 . 由于您的 UpdateService 不是上下文的一部分,因此不会进行处理 .

    您的设置看起来有点像单元和集成测试的不清楚的混合 . 对于单元测试,您根本不需要 spring 环境 . 只需使 @Value 带注释的成员包受到保护并设置它们或使用 ReflectionTestUtils.setField() (均显示):

    public class UpdateServiceTest {
    
       @InjectMocks
       private UpdateService updateService;
    
       @Mock
       private UserService mockUserService;
    
       @Before
       public void setUp() {
          MockitoAnnotations.initMocks(this);
          ReflectionTestUtils.setField(updateService, "successTable", "my_success");
          updateService.failedTable = "my_failures";
       }
    }
    

    对于集成测试,所有接线应通过 spring 完成 .

    为此,我添加了一个提供模拟用户服务的内部配置类( @Primary 仅适用于您在上下文中有任何其他用户服务的情况),并且模拟存储在静态成员中,以便从中轻松访问模拟之后进行测试 .

    @RunWith(SpringJUnite4ClassRunner.class)
    @SpringApplicationConfiguration(classes = {TestApplication.class, UpdateServiceTest.TestAddOn.class})
    @WebAppConfiguration
    public class UpdateServiceTest {
    
       @Autowired
       private UpdateService updateService;
    
       private static UserService mockUserService;
    
    
    
       static class TestAddOn {
          @Bean
          @Primary
          UserService updateService() {
            mockUserService = Mockito.mock(UserService.class);
            return mockUserService;
          }
       }
    }
    

相关问题