2012-02-10 107 views
5

我刚接触Spring框架,并对使用Spring Context的依赖注入功能有疑问。在每个单元测试之后创建一个bean的新实例

这是我试图写一个集成测试类:

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

你有多种测试方法,或只是你粘贴的那个? – ggreiner 2012-02-10 07:50:30

+0

我有四种测试方法(其中1个已粘贴)。我得到三个失败的测试,说我已经调用了我试图分别测试2,3和4次的方法。 – geoffreydv 2012-02-10 07:53:48

回答

6

要清楚地区分单元和集成测试(跳过辩论 - 你可以用两种方式测试你的服务:

  • 通过集成测试 - 你启动整个Spring Context并测试服务为单身bean。
  • 通过单元测试 - 您只需自己初始化服务,模拟需要嘲笑的内容,而不需要Spring。

我的建议是不要混淆Spring和嘲笑,如果你能帮助它 - 保持的Mockito单元测试(这是你需要它的外观是什么),并使用该引导用于测试的整个Spring上下文集成测试其他的东西 - 持久性问题,交易等等。

你不需要Spring来模拟一个类的合作者,并用Mockito做简单的交互测试。

+1

“我的建议是不要混合使用Spring和Mock,如果你可以帮助的话”。如果您使用setter或使用Spring的ReflectionTestUtils.setField()方法,则手动注入您的模拟。如果您的服务在每次测试中都发生变化,则无需启动弹簧环境 – 2012-02-10 13:37:51

+0

您的建议帮助我清除了一些问题。我是在滥用“集成测试”这个词,而我真正的意思是交互测试。我原本选择不使用spring来注入我的mock,所以我现在切换回那个代码,只用spring来进行真正的集成测试。谢谢! – geoffreydv 2012-02-10 20:01:29

2

在你的@Before方法中,一定要重置你的模拟对象。

@Before 
public void setup(){ 
    Mockito.reset(validator); 
} 
+0

由于某些原因,其中一个测试现在已经修复,但其他测试仍然失败(与之前相同的信息)。我在这样的设置方法中放置了4行:Mockito.reset(userService.getValidator());也许它失败了,因为我使用了一个getter?我只在UserService的测试类中有一个属性,而不是4个单独的对象。 – geoffreydv 2012-02-10 08:30:32

0

您可以尝试在您的测试方法中调用setDirty(true)来重新加载Spring上下文。

0

我从来没有使用Mockito,但Spring-Beans默认是Singletons,所以它们不会被重新创建,除非您在Spring-Container上调用refresh()

如果你反正不需要他们是单身,你可以设置自己的范围prototype这将在每次注射创造新豆实例...

24

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

我会假设你使用的是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作为一个单元的行为,所以你通过使用mock来隔离它。这也有一个好处,即JUnit通过对每个测试使用不同的测试类实例来隔离您的测试,因此除非您使用static或其他不友好的测试方法,否则所有测试都会正常工作。

  • 在集成测试中,使用真正的bean并让Spring注入。这里的目标是验证bean与环境(数据库,网络,...)互相良好地相互作用。你做而不是想在这里分离bean,所以你不应该使用mocks。

查看Spring documentation about testing的更详细的解释。

+0

感谢您的代码示例。正如我对Eugen的回答评论的那样,我把这两种测试混合起来。这是我将在我的真正的集成测试中使用:) – geoffreydv 2012-02-10 20:05:20

+0

非常感谢。这也可以应用于扩展'AbstractTestNGSpringContextTests'的TestNG-Testclasses。 – 2013-11-21 18:50:20

+0

AFTER_EACH_TEST_METHOD,真棒。这确实有助于我的每个测试的新页面对象。 – Will 2015-11-07 02:33:41

相关问题