在每个单元测试后创建一个新的 Bean 实例

2022-09-03 08:40:59

我是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>

有什么想法吗?


答案 1

您正在重用您的上下文,为了使测试彼此独立,您可能需要在每次测试后刷新上下文以重置所有内容。

我会假设你正在使用Junit 4.5 +。它与其他测试框架类似。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"mycontext.xml"})
@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
public class MyTestClass {
...
    // my tests    
...
}

如果需要“修复”的方法很少,则可以将 放在方法级别,但是如果您使用的是Spring,则最好的选择是在每次测试后进行。@DirtiesContext

无论如何,我认为你不应该在集成测试中使用模拟/间谍:

  • 在单元测试中,使用模拟(如果需要)并手动注入。在这里,您希望将测试的Bean的行为作为一个单元进行验证,因此您可以使用模拟将其与其他Bean隔离开来。这还具有一个优点,即 JUnit 通过为每个测试使用测试类的不同实例来隔离测试,因此除非您使用或其他测试不友好的实践,否则一切都会正常工作。static

  • 在集成测试中,使用真正的豆子并让Spring注入。这里的目标是验证Bean是否彼此/与环境(数据库,网络等)具有良好的交互性。您不想在这里隔离豆子,因此不应使用模拟。

有关更详细的说明,请参阅有关测试的春季文档


答案 2

要清楚地将单元测试和集成测试分开(跳过关于每个类别含义的争论) - 您可以通过两种方式测试您的服务:

  • 通过集成测试 - 启动整个Spring Context并将服务作为单例bean进行测试。
  • 通过单元测试 - 您只需自己初始化服务,模拟需要模拟的内容,而无需Spring。

我的建议是,如果你能帮助它,不要混合Spring和mocks - 保留Mockito进行单元测试(这是你需要的),并使用集成测试来引导整个Spring上下文来测试其他东西 - 持久性问题,事务等。

你不需要Spring来嘲笑一个班级的合作者,并用Mockito做简单的交互测试。


推荐