簡體   English   中英

使用Spring + JPA但沒有可用數據庫的單元測試Service Bean

[英]Unit-testing Service bean which uses Spring+JPA but no DB is available

一個簡單的銀行應用程序: 在此處輸入圖片說明

注意事項:

  • 將Spring + JPA與EclipseLink一起用作JPA提供程序
  • 使用@PersistenceContext將EntityManager注入BaseDaoImpl
  • DAO被自動連接到Service Bean中
  • 服務方法中使用的@Transactional注釋

目標:作為Maven構建的一部分的單元測試服務方法

單元測試=簡單的API測試。 例如,服務方法: transfer(int fromAccountId, int toAccountId, double amount)具有單元測試用例:

  • fromAccountId不應為0
  • toAccountId不應為0
  • fromAccountId != toAccountId
  • `金額大於0
  • 等等

這些“單元測試”情況不需要DB連接。

問題:構建服務器沒有數據庫設置。 但是,當執行單元測試用例時,Spring嘗試連接到失敗的DB。 但是,對於這些情況,我們確實不需要數據庫連接。 (我們還有另一套“集成案例”-這些不是作為常規構建的一部分執行的,而是在可用的完整環境下手動執行的。如何?-請參見此線程

問題:

  • 執行此類單元測試用例的最佳實踐是什么?
  • 我們可以強迫Spring在實際需要的最后一刻才建立數據庫連接嗎? (現在,它遇到@Transactional方法時會執行此操作)

根據要求添加服務層代碼:

public class BankManagerImpl implements BankManager {

    @Autowired
    AccountDao accountDao;

    @Autowired
    TransactionDao transactionDao;

    ...

    @Override
    @Transactional
    public void deposit(int accountId, double amount) {
        Account a = accountDao.getAccount(accountId);
        double bal = a.getAmount();
        bal = bal + amount;
        a.setAmount(bal);

        accountDao.updateAccount(a);

        transactionDao.addTransaction(a, TransactionDao.DEPOSIT, amount);
    }

    @Override
    @Transactional
    public void withdraw(int accountId, double amount) {
        Account a = accountDao.getAccount(accountId);

        double bal = a.getAmount();
        if(bal < amount) {
            throw new RuntimeException("insufficient balance");
        }

        bal = bal - amount;
        a.setAmount(bal);

        accountDao.updateAccount(a);

        transactionDao.addTransaction(a, TransactionDao.WITHDRAW, amount);
    }

    @Override
    @Transactional
    public void transfer(int fromAccountId, int toAccountId, double amount) {
        withdraw(fromAccountId, amount);
        deposit(toAccountId, amount);
    }

    ...    

}

您需要的是集成測試。 首先,您必須構建一個虛擬的 PlatformManager並確保將其用於測試。 您將在SO上的另一篇文章中找到有關此方面的線索。 我如何在JUnit測試中模擬TransactionManager(在容器外部)? 以及下面的另一個(部分)示例。

當spring按順序應用ApplicationContext定義時(最后一個覆蓋其他),您只需在最后一個位置添加一個XML(或JavaConfig)文件測試,以聲明虛擬PlatformManager具有與常規配置中相同的bean名稱。

然后,您從應用程序上下文中獲取服務bean,並用模擬(Mockito或您喜歡的東西)替換其Dao。

根據您要測試的內容,您必須調整虛擬的PlatformManager ,但是如果您簡單地添加:

public class MockedTransactionManager implements PlatformTransactionManager {
public boolean transactionStarted = false
public commited = false;
public rollbacked = false;

@Override
public TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
    transactionStarted = true;
    return null;
}

@Override
public void commit(TransactionStatus status) throws TransactionException {
    commited = true;
}

@Override
public void rollback(TransactionStatus status) throws TransactionException {
    commited = true;
}

您將能夠控制是開始,提交還是回滾事務。 如果有特殊要求,則可能必須創建一個真實的SimpleTransactionStatus而不是傳遞null。

目的是在單元測試期間禁用數據庫連接。 一種方法是@Serge Ballesta的答案中提到的模擬交易經理。 我們發現了一種更簡單的方法-在單元測試期間,我們完全禁用了加載事務管理器。 這可以通過在應用程序上下文中注釋掉下面的行來完成,這可以防止基於注釋的事務被啟動。

<!-- <tx:annotation-driven transaction-manager="transactionManager" /> -->

我建議您完全放棄為數據庫交互編寫單元測試。

這個這個博客文章解釋了為什么。

使用Spring時,僅在測試環境中使用內存數據庫,然后繼續編寫有意義的集成測試非常容易。

在您要為BankServiceImpl編寫單元測試的用BankServiceImpl ,根本不需要參與Spring。

您需要做的就是自行創建該服務,並將相關的模擬信息注入其中。

編寫這樣的測試將使您不必再模擬與Spring相關的服務(例如事務管理),並且使測試變得非常快捷,因為將不需要創建任何Spring上下文。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM