我正在使用服务中的自动构造函数,当在测试类中实例化会导致@Value注释返回null。自动化依赖项直接解决了问题,但该项目遵循了使用基于构造函数自动引导的约定。我的理解是,在测试类中实例化服务不是从春季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);
}
}
使@Value
工作updateService
应在春季上下文中。
春季框架集成测试的最佳实践是在测试上下文中包括应用程序上下文,并在测试中自动化测试来源:
...
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);
}
@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
仅适用于您在上下文中具有任何其他用户服务的情况),并且该模拟存储在此处的静态成员中,可以简单地访问之后从测试中模拟。
@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;
}
}
}