我是Spring框架的新手,并且使用Spring Context对其依赖注入功能有疑问。
这是我正在尝试编写集成测试的类:
public class UserService {
private Validator validator;
private UserRepository userRepository;
private Encryptor encryptor;
private MailService mailService;
...
public void registerUser(User user) {
user.setPassword(encryptor.encrypt(user.getPassword()));
Errors errors = new BindException(user, "user");
validator.validate(user, errors);
if (errors.getErrorCount() == 0) {
userRepository.addUser(user);
mailService.sendMail(user.getEmail());
}
}
在我的测试中(使用Mockito)我想确保调用这四个项目,因此我创建了如下测试:
public void testRegisterCallsValidateInValidator() {
userService.registerUser(testUser);
verify(userService.getValidator(), times(1)).validate(any(User.class), any(Errors.class));
}
然而,所有测试都失败了,说我多次调用该方法。我唯一的猜测是UserService bean在所有测试开始时创建一次,但在每次测试后都没有重新加载。
在我的测试配置中,我使用以下xml来决定要注入的bean:
<bean id="userService" class="be.kdg.coportio.services.UserService">
<property name="validator" ref="validator"/>
<property name="userRepository" ref="userRepository"/>
<property name="encryptor" ref="encryptor"/>
<property name="mailService" ref="mailService"/>
</bean>
有什么想法吗?
答案 0 :(得分:28)
您正在重复使用上下文,为了让测试彼此独立,您可能需要在每次测试后刷新上下文以重置所有内容。
我认为您使用的是Junit 4.5+。它与其他测试框架类似。
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"mycontext.xml"})
@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
public class MyTestClass {
...
// my tests
...
}
如果需要“修复”的方法很少,可以将@DirtiesContext
放在方法级别,但如果你使用的是Spring,那么最好的选择是在每次测试后都这样做。
无论如何,我认为你不应该在集成测试中使用模拟/间谍:
在单元测试中,使用模拟(如果需要)并手动注入。在这里,您要验证测试bean作为一个单元的行为,因此您可以使用模拟将其与其余bean隔离。这也有一个优点,即JUnit通过为每个测试使用不同的测试类实例来隔离您的测试,所以除非您使用static
或其他测试不友好的实践,否则一切都会正常工作。
在集成测试中,使用真正的bean并让Spring注入。这里的目标是验证bean彼此之间/与环境(数据库,网络......)的良好交互。不想要在这里隔离bean,所以你不应该使用模拟。< / p>
有关详细说明,请参阅Spring documentation about testing。
答案 1 :(得分:9)
要明确区分单元和集成测试(跳过对每个类别的含义的争论) - 您可以通过两种方式测试您的服务:
我的建议是不要混合Spring和mocks如果你可以帮助它 - 保持Mockito进行单元测试(这是你需要的东西)并使用集成测试来引导整个Spring上下文来测试其他东西 - 持久性问题,交易等。
你不需要Spring来模拟一个类的合作者,并且可以与Mockito进行简单的交互测试。
答案 2 :(得分:2)
在@Before
方法中,请务必重置模拟对象。
@Before
public void setup(){
Mockito.reset(validator);
}
答案 3 :(得分:0)
您可以尝试在测试方法中调用setDirty(true)来重新加载Spring上下文。
答案 4 :(得分:0)
我从未使用过Mockito,但Spring-Beans默认是单例 - 所以除非你在Spring-Container上调用refresh()
,否则不会重新创建它们。
如果你仍然不需要它们成为Singletons,你可以将它们的范围设置为prototype
,这将在每次注入时创建新的bean实例...