原創|使用教程|編輯:鄭恭琳|2020-12-10 13:32:28.137|閱讀 306 次
概述:Java中的模擬是什么?只需單擊一下按鈕,即可自動生成單元測試,包括所有模擬和驗證。 好的單元測試是確保您的代碼在今天能正常工作,并在將來繼續有效的好方法。全面的測試套件具有良好的基于代碼和基于行為的覆蓋范圍,可以為組織節省大量時間和麻煩。但是,看到項目編寫的測試不夠多的情況并不少見。實際上,一些開發人員甚至一直在完全反對使用它們。
# 界面/圖表報表/文檔/IDE等千款熱門軟控件火熱銷售中 >>
相關鏈接:
Java中的模擬是什么?只需單擊一下按鈕,即可自動生成單元測試,包括所有模擬和驗證。
好的單元測試是確保您的代碼在今天能正常工作,并在將來繼續有效的好方法。全面的測試套件具有良好的基于代碼和基于行為的覆蓋范圍,可以為組織節省大量時間和麻煩。但是,看到項目編寫的測試不夠多的情況并不少見。實際上,一些開發人員甚至一直在完全反對使用它們。
開發人員未編寫足夠的單元測試的原因有很多。最大的原因之一是它們需要花費大量的時間來構建和維護,尤其是在大型、復雜的項目中。在復雜的項目中,單元測試通常需要實例化和配置許多對象。這需要花費很多時間來設置,并且可能使測試本身比所測試的代碼復雜(或更復雜)。
讓我們看一下Java中的示例:
public LoanResponse requestLoan(LoanRequest loanRequest, LoanStrategy strategy) { LoanResponse response = new LoanResponse(); response.setApproved(true); if (loanRequest.getDownPayment().compareTo(loanRequest.getAvailableFunds()) > 0) { response.setApproved(false); response.setMessage("error.insufficient.funds.for.down.payment"); return response; } if (strategy.getQualifier(loanRequest) < strategy.getThreshold(adminManager)) { response.setApproved(false); response.setMessage(getErrorMessage()); } return response; }
在這里,我們有一個處理LoanRequest并生成LoanResponse的方法。請注意LoanStrategy參數,該參數用于處理LoanRequest。策略對象可能很復雜——它可能訪問數據庫、外部系統或引發RuntimeException。要為requestLoan()編寫測試,我需要擔心要測試使用哪種類型的LoanStrategy,并且可能需要使用各種LoanStrategy實現和LoanRequest配置來測試我的方法。
forrequestLoan()的單元測試可能如下所示:
@Test public void testRequestLoan() throws Throwable { // Set up objects DownPaymentLoanProcessor processor = new DownPaymentLoanProcessor(); LoanRequest loanRequest = LoanRequestFactory.create(1000, 100, 10000); LoanStrategy strategy = new AvailableFundsLoanStrategy(); AdminManager adminManager = new AdminManagerImpl(); underTest.setAdminManager(adminManager); Map<String, String> parameters = new HashMap<>(); parameters.put("loanProcessorThreshold", "20"); AdminDao adminDao = new InMemoryAdminDao(parameters); adminManager.setAdminDao(adminDao); // Call the method under test LoanResponse response = processor.requestLoan(loanRequest, strategy); // Assertions and other validations }
如您所見,我的測試中有一整段內容只是創建對象和配置參數。查看requestLoan()方法并不明顯,需要設置哪些對象和參數。為了創建此示例,我必須運行測試,添加一些配置,然后再次運行并一遍又一遍地重復該過程。我不得不花太多時間弄清楚如何配置AdminManager和LoanStrategy,而不是專注于我的方法以及在那里需要測試的內容。而且,我仍然需要擴展測試以涵蓋AdminDao的更多LoanRequest案例、更多策略和更多參數。
另外,通過使用真實的對象進行測試,我的測試實際上不僅驗證了requestLoan()的行為——還取決于AvailableFundsLoanStrategy,AdminManagerImpl和AdminDao的行為才能運行我的測試。實際上,我也在測試這些類。在某些情況下,這是理想的,但在其他情況下則不是。另外,如果其他類之一發生更改,即使requestLoan()的行為未更改,測試也可能開始失敗。對于此測試,我們寧愿將被測類與其依賴項隔離。
解決復雜性問題的一種方法是模擬那些復雜的對象。對于此示例,我將從對LoanStrategy參數使用模擬開始:
@Test public void testRequestLoan() throws Throwable { // Set up objects DownPaymentLoanProcessor processor = new DownPaymentLoanProcessor(); LoanRequest loanRequest = LoanRequestFactory.create(1000, 100, 10000); LoanStrategy strategy = Mockito.mock(LoanStrategy.class); Mockito.when(strategy.getQualifier(any(LoanRequest.class))).thenReturn(20.0d); Mockito.when(strategy.getThreshold(any(AdminManager.class))).thenReturn(20.0d); // Call the method under test LoanResponse response = processor.requestLoan(loanRequest, strategy); // Assertions and other validations }
讓我們看看這里發生了什么。我們使用Mockito.mock()創建一個LoanStrategy的模擬實例。因為我們知道該策略將調用getQualifier()和getThreshold(),所以我們使用Mockito.when(…).thenReturn()定義了這些調用的返回值。對于此測試,我們不在乎LoanRequest實例的值是什么,也不再需要真正的AdminManager,因為AdminManager僅由真正的LoanStrategy使用。
此外,由于我們沒有使用真正的LoanStrategy,所以我們不在乎LoanStrategy的具體實現會做什么。我們不需要設置測試環境,依賴項或復雜的對象。我們專注于測試requestLoan(),而不是LoanStrategy或AdminManager。被測方法的代碼流直接由模擬程序控制。
使用Mockito編寫此測試要比創建一個復雜的LoanStrategy實例要容易得多。但是仍然存在一些挑戰:
我們使用了Parasoft Jtest來幫助解決上述挑戰。單元測試模塊Parasoft Jtest是用于Java測試的企業解決方案,可幫助開發人員管理Java軟件開發的風險。
在單元測試方面,Parasoft Jtest可幫助您自動化使用模擬創建和維護單元測試中最困難的部分。對于上面的示例,它可以通過單擊一次按鈕自動為requestLoan()生成測試,包括您在示例測試中看到的所有模擬和驗證。
在這里,我使用了Parasoft Jtest單元測試助手工具欄中的“常規”操作來生成以下測試:
@Test public void testRequestLoan() throws Throwable { // Given DownPaymentLoanProcessor underTest = new DownPaymentLoanProcessor(); // When double availableFunds = 0.0d; // UTA: default value double downPayment = 0.0d; // UTA: default value double loanAmount = 0.0d; // UTA: default value LoanRequest loanRequest = LoanRequestFactory.create(availableFunds, downPayment, loanAmount); LoanStrategy strategy = mockLoanStrategy(); LoanResponse result = underTest.requestLoan(loanRequest, strategy); // Then // assertNotNull(result); }
此測試的所有模擬都在輔助方法中進行:
private static LoanStrategy mockLoanStrategy() throws Throwable { LoanStrategy strategy = mock(LoanStrategy.class); double getQualifierResult = 0.0d; // UTA: default value when(strategy.getQualifier(any(LoanRequest.class))).thenReturn(getQualifierResult); double getThresholdResult = 0.0d; // UTA: default value when(strategy.getThreshold(any(AdminManager.class))).thenReturn(getThresholdResult); return strategy; }
為我設置了所有必需的模擬——Parasoft Jtest檢測到對getQualifier()和getThreshold()的方法調用,并模擬了這些方法。一旦在單元測試中為availableFunds,downPayment等配置了值,就可以運行測試了(我也可以生成參數化測試以更好地覆蓋!)。另請注意,該助手會通過其注釋“UTA:默認值”為更改哪些值提供一些指導,從而使測試更加容易。
這樣可以節省生成測試的大量時間,尤其是在我不知道需要模擬什么或如何使用Mockito API的情況下。
當應用程序邏輯更改時,測試通常也需要更改。如果測試寫得很好,則在不更新測試的情況下更新代碼將導致測試失敗。通常,更新測試中的最大挑戰是了解需要更新的內容以及如何準確執行該更新。如果存在大量的模擬和值,則可能很難找到必要的更改。
為了說明這一點,讓我們對測試中的代碼進行一些更改:
public LoanResponse requestLoan(LoanRequest loanRequest, LoanStrategy strategy) { ... String result = strategy.validate(loanRequest); if (result != null && !result.isEmpty()) { response.setApproved(false); response.setMessage(result); return response; } ... return response; }
我們向LoanStrategy添加了一個新方法validate(),現在可以從requestLoan()調用它。可能需要更新測試以指定validate()應該返回什么。
在不更改生成的測試的情況下,讓我們在Parasoft Jtest單元測試助手中運行它:
在我的測試運行期間,Parasoft Jtest檢測到在模擬的LoanStrategy參數上調用了validate()。由于尚未為模擬設置方法,因此助手建議我模擬validate()方法。“模擬”快速修復操作會自動更新測試。這是一個簡單的示例,但是對于復雜的代碼,很難找到丟失的模擬,建議和快速修復可以為我們節省大量調試時間。
使用快速修復更新測試后,我可以看到新的模擬并為validateResult設置所需的值:
private static LoanStrategy mockLoanStrategy() throws Throwable { LoanStrategy strategy = mock(LoanStrategy.class); String validateResult = ""; // UTA: default value when(strategy.validate(any(LoanRequest.class))).thenReturn(validateResult); double getQualifierResult = 20.0d; when(strategy.getQualifier(any(LoanRequest.class))).thenReturn(getQualifierResult); double getThresholdResult = 20.0d; when(strategy.getThreshold(any(AdminManager.class))).thenReturn(getThresholdResult); return strategy; }
我可以使用一個非空值配置validateResult,以測試該方法輸入新代碼塊的用例,或者可以使用空值(或null)來驗證未輸入新塊時的行為。
助手還提供了一些有用的工具來分析測試流程。例如,這是我們測試運行的流程樹:
Parasoft Jtest單元測試助手的流程樹,顯示在測試執行期間進行的調用
運行測試時,我可以看到測試為LoanStrategy創建了一個新的模擬,并模擬了validate(),getQualifier()和getThreshold()方法。我可以選擇方法調用,并查看(在“變量”視圖中)向該調用發送了哪些參數,以及返回了什么值(或拋出了異常)。在調試測試時,這比挖掘日志文件更容易使用和理解。
自此,您可以自動化單元測試的許多方面。Parasoft Jtest可幫助您以更少的時間和精力來生成單元測試,從而幫助您降低與模擬相關的復雜性。它還提出了許多其他建議來改進基于運行時數據的現有測試,并支持參數化測試,Spring Application測試和PowerMock(用于模擬靜態方法和構造函數)。如果您想要在自己的環境中進行試用,可以在。
本站文章除注明轉載外,均為本站原創或翻譯。歡迎任何形式的轉載,但請務必注明出處、不得修改原文相關鏈接,如果存在內容上的異議請郵件反饋至chenjj@fc6vip.cn