【发布时间】:2016-01-22 23:45:42
【问题描述】:
我已经阅读了大量关于如何模拟 Spring 的 bean 及其自动装配字段的文章。但是我找不到关于自动装配的 bean 列表的任何信息。
具体问题
我有一个名为FormValidatorManager 的课程。这个类循环通过几个实现IFormValidator的验证器。
@Component
public class FormValidatorManager implements IValidatorManager {
@Autowired
private List<IFormValidator> validators;
@Override
public final IFieldError validate(ColumnDTO columnToValidate, String sentValue) {
String loweredColName = columnToValidate.getName().toLowerCase();
IFieldError errorField = new FieldError(loweredColName);
for (IEsmFormValidator validator : validators) {
List<String> errrorsFound = validator.validate(columnToValidate, sentValue);
//les erreurs ne doivent pas être cumulées.
if(CollectionUtils.isNotEmpty(errrorsFound)){
errorField.addErrors(errrorsFound);
break;
}
}
return errorField;
}
}
我想测试这个类。但我找不到模拟 validators 属性的方法。
我的尝试
由于IFormValidators 是单例,我尝试模拟这些bean 的几个实例,希望它们反映在FormValidatorManager.validators 中,但没有成功。
然后,我尝试创建一个IFormValidators 的列表,该列表被注释为@Mock。通过手动启动List,我希望initMocks() 注入创建的列表。那还是没有成功。
这是我最后一次尝试:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:spring/test-validator-context.xml"})
public class FormValidatorManagerTest {
@Mock
private RegexValidator regexValidator;
@Mock
private FormNotNullValidator notNullValidator;
@Mock
private FormDataTypeValidator dataValidator;
@InjectMocks
private FormValidatorManager validatorManager;
@Mock
private List<IEsmFormValidator> validators = new ArrayList<IEsmFormValidator>();
@Mock
private ColumnDTO columnDTO;
@Before
public void init() {
validators.add(notNullValidator);
validators.add(regexValidator);
validators.add(dataValidator);
MockitoAnnotations.initMocks(this);
Mockito.when(columnDTO.getTitle()).thenReturn("Mock title");
Mockito.when(columnDTO.getName()).thenReturn("Mock name");
}
@Test
public void testNoErrorFound(){
mockValidator(notNullValidator, new ArrayList<String>());
mockValidator(regexValidator, new ArrayList<String>());
mockValidator(dataValidator, new ArrayList<String>());
IFieldError fieldErrors = validatorManager.validate(columnDTO, "Not null value");
Assert.assertEquals(0, fieldErrors.getErrors().size());
verifyNumberOfValidateCalls(regexValidator, Mockito.atMost(1));
verifyNumberOfValidateCalls(dataValidator, Mockito.atMost(1));
verifyNumberOfValidateCalls(notNullValidator, Mockito.atMost(1));
}
private void mockValidator(IFormValidator validator, List<String> listToReturn){
Mockito.when(validator.validate(Mockito.any(ColumnDTO.class), Mockito.anyString())).thenReturn( listToReturn );
}
private void verifyNumberOfValidateCalls(IFormValidator validator, VerificationMode verifMode){
Mockito.verify(validator, verifMode).validate(Mockito.any(ColumnDTO.class), Mockito.anyString());
}
}
在IFormValidator.validate() 中抛出了一个 NPE,我认为它会被嘲笑。不应该调用具体的实现。
这会导致非常糟糕的行为,因为我对该类的一些测试是误报,而另一些则完全失败。
我正在尝试弄清楚如何模拟自动装配的 bean 列表,同时仍然可以模拟特定的实现。
你有一个解决方案的想法开始吗?
问候
【问题讨论】:
-
您希望
@Mock和@Autowired在单个字段上如何工作?你想要它被嘲笑,还是自动连接?同时在您的测试中创建一个列表并希望 spring 检测到它永远不会发生。在你的 spring 配置而不是你的类中创建模拟,为此使用Mockito的mock工厂方法。 -
我实际上希望它被嘲笑,但既然它是一个弹簧豆,也不应该被自动装配吗?我实际上试图删除自动连线,但它也不起作用。更糟糕的是,验证器为空且未模拟。
-
你可以使用@Autowired 上面的构造函数。所以在测试中你可以将验证器作为构造函数参数传递给被测试的类。
-
我不能更改测试的类实现
-
它要么是一个mock bean,要么是一个spring bean,不能两者兼而有之。你可以让你的 mock 自动连接,但只有当你将 Mocks 定义为 spring bean 时才会起作用。您可能想阅读olivergierke.de/2013/11/why-field-injection-is-evil 并重新考虑字段注入。我会将字段设为 final 并简单地创建一个构造函数。这样你就可以真正让它成为一个单元测试并自己构建对象并将任何你想要的东西传递给构造函数,你可以提高代码质量。
标签: java spring unit-testing mockito autowired