Spring @Autowired构造函数导致@Value在测试类

时间:2016-06-22 17:56:51

标签: java spring mockito autowired spring-test

我在服务中使用自动装配的构造函数,当在测试类中实例化时,会导致@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 个答案:

答案 0 :(得分:2)

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

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

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

模拟用户服务

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);
}

答案 1 :(得分:2)

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

您的设置看起来有点像单元和集成测试的混合不清楚。对于单元测试,您根本不需要弹簧环境。只需使UpdateService带注释的成员包受到保护并设置它们或使用@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"; } } 仅适用于您在上下文中有任何其他用户服务的情况)并且模拟存储在此处的静态成员中之后从测试中简单访问模拟。

@Primary