2012-02-10 83 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

相關問題