原創(chuàng)|使用教程|編輯:鄭恭琳|2020-12-10 13:32:28.137|閱讀 306 次
概述:Java中的模擬是什么?只需單擊一下按鈕,即可自動生成單元測試,包括所有模擬和驗證。 好的單元測試是確保您的代碼在今天能正常工作,并在將來繼續(xù)有效的好方法。全面的測試套件具有良好的基于代碼和基于行為的覆蓋范圍,可以為組織節(jié)省大量時間和麻煩。但是,看到項目編寫的測試不夠多的情況并不少見。實際上,一些開發(fā)人員甚至一直在完全反對使用它們。
# 界面/圖表報表/文檔/IDE等千款熱門軟控件火熱銷售中 >>
相關(guān)鏈接:
	 
Java中的模擬是什么?只需單擊一下按鈕,即可自動生成單元測試,包括所有模擬和驗證。
好的單元測試是確保您的代碼在今天能正常工作,并在將來繼續(xù)有效的好方法。全面的測試套件具有良好的基于代碼和基于行為的覆蓋范圍,可以為組織節(jié)省大量時間和麻煩。但是,看到項目編寫的測試不夠多的情況并不少見。實際上,一些開發(fā)人員甚至一直在完全反對使用它們。
	
開發(fā)人員未編寫足夠的單元測試的原因有很多。最大的原因之一是它們需要花費大量的時間來構(gòu)建和維護,尤其是在大型、復(fù)雜的項目中。在復(fù)雜的項目中,單元測試通常需要實例化和配置許多對象。這需要花費很多時間來設(shè)置,并且可能使測試本身比所測試的代碼復(fù)雜(或更復(fù)雜)。
讓我們看一下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參數(shù),該參數(shù)用于處理LoanRequest。策略對象可能很復(fù)雜——它可能訪問數(shù)據(jù)庫、外部系統(tǒng)或引發(fā)RuntimeException。要為requestLoan()編寫測試,我需要擔(dān)心要測試使用哪種類型的LoanStrategy,并且可能需要使用各種LoanStrategy實現(xiàn)和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 } 
	
如您所見,我的測試中有一整段內(nèi)容只是創(chuàng)建對象和配置參數(shù)。查看requestLoan()方法并不明顯,需要設(shè)置哪些對象和參數(shù)。為了創(chuàng)建此示例,我必須運行測試,添加一些配置,然后再次運行并一遍又一遍地重復(fù)該過程。我不得不花太多時間弄清楚如何配置AdminManager和LoanStrategy,而不是專注于我的方法以及在那里需要測試的內(nèi)容。而且,我仍然需要擴展測試以涵蓋AdminDao的更多LoanRequest案例、更多策略和更多參數(shù)。
另外,通過使用真實的對象進行測試,我的測試實際上不僅驗證了requestLoan()的行為——還取決于AvailableFundsLoanStrategy,AdminManagerImpl和AdminDao的行為才能運行我的測試。實際上,我也在測試這些類。在某些情況下,這是理想的,但在其他情況下則不是。另外,如果其他類之一發(fā)生更改,即使requestLoan()的行為未更改,測試也可能開始失敗。對于此測試,我們寧愿將被測類與其依賴項隔離。
	
解決復(fù)雜性問題的一種方法是模擬那些復(fù)雜的對象。對于此示例,我將從對LoanStrategy參數(shù)使用模擬開始:
@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
}
讓我們看看這里發(fā)生了什么。我們使用Mockito.mock()創(chuàng)建一個LoanStrategy的模擬實例。因為我們知道該策略將調(diào)用getQualifier()和getThreshold(),所以我們使用Mockito.when(…).thenReturn()定義了這些調(diào)用的返回值。對于此測試,我們不在乎LoanRequest實例的值是什么,也不再需要真正的AdminManager,因為AdminManager僅由真正的LoanStrategy使用。
此外,由于我們沒有使用真正的LoanStrategy,所以我們不在乎LoanStrategy的具體實現(xiàn)會做什么。我們不需要設(shè)置測試環(huán)境,依賴項或復(fù)雜的對象。我們專注于測試requestLoan(),而不是LoanStrategy或AdminManager。被測方法的代碼流直接由模擬程序控制。
使用Mockito編寫此測試要比創(chuàng)建一個復(fù)雜的LoanStrategy實例要容易得多。但是仍然存在一些挑戰(zhàn):
	
我們使用了Parasoft Jtest來幫助解決上述挑戰(zhàn)。單元測試模塊Parasoft Jtest是用于Java測試的企業(yè)解決方案,可幫助開發(fā)人員管理Java軟件開發(fā)的風(fēng)險。
在單元測試方面,Parasoft Jtest可幫助您自動化使用模擬創(chuàng)建和維護單元測試中最困難的部分。對于上面的示例,它可以通過單擊一次按鈕自動為requestLoan()生成測試,包括您在示例測試中看到的所有模擬和驗證。
	
在這里,我使用了Parasoft Jtest單元測試助手工具欄中的“常規(guī)”操作來生成以下測試:
@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;
}
為我設(shè)置了所有必需的模擬——Parasoft Jtest檢測到對getQualifier()和getThreshold()的方法調(diào)用,并模擬了這些方法。一旦在單元測試中為availableFunds,downPayment等配置了值,就可以運行測試了(我也可以生成參數(shù)化測試以更好地覆蓋!)。另請注意,該助手會通過其注釋“UTA:默認(rèn)值”為更改哪些值提供一些指導(dǎo),從而使測試更加容易。
這樣可以節(jié)省生成測試的大量時間,尤其是在我不知道需要模擬什么或如何使用Mockito API的情況下。
	
當(dāng)應(yīng)用程序邏輯更改時,測試通常也需要更改。如果測試寫得很好,則在不更新測試的情況下更新代碼將導(dǎo)致測試失敗。通常,更新測試中的最大挑戰(zhàn)是了解需要更新的內(nèi)容以及如何準(zhǔn)確執(zhí)行該更新。如果存在大量的模擬和值,則可能很難找到必要的更改。
為了說明這一點,讓我們對測試中的代碼進行一些更改:
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(),現(xiàn)在可以從requestLoan()調(diào)用它。可能需要更新測試以指定validate()應(yīng)該返回什么。
在不更改生成的測試的情況下,讓我們在Parasoft Jtest單元測試助手中運行它:
	
在我的測試運行期間,Parasoft Jtest檢測到在模擬的LoanStrategy參數(shù)上調(diào)用了validate()。由于尚未為模擬設(shè)置方法,因此助手建議我模擬validate()方法。“模擬”快速修復(fù)操作會自動更新測試。這是一個簡單的示例,但是對于復(fù)雜的代碼,很難找到丟失的模擬,建議和快速修復(fù)可以為我們節(jié)省大量調(diào)試時間。
使用快速修復(fù)更新測試后,我可以看到新的模擬并為validateResult設(shè)置所需的值:
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單元測試助手的流程樹,顯示在測試執(zhí)行期間進行的調(diào)用
運行測試時,我可以看到測試為LoanStrategy創(chuàng)建了一個新的模擬,并模擬了validate(),getQualifier()和getThreshold()方法。我可以選擇方法調(diào)用,并查看(在“變量”視圖中)向該調(diào)用發(fā)送了哪些參數(shù),以及返回了什么值(或拋出了異常)。在調(diào)試測試時,這比挖掘日志文件更容易使用和理解。
	
自此,您可以自動化單元測試的許多方面。Parasoft Jtest可幫助您以更少的時間和精力來生成單元測試,從而幫助您降低與模擬相關(guān)的復(fù)雜性。它還提出了許多其他建議來改進基于運行時數(shù)據(jù)的現(xiàn)有測試,并支持參數(shù)化測試,Spring Application測試和PowerMock(用于模擬靜態(tài)方法和構(gòu)造函數(shù))。如果您想要在自己的環(huán)境中進行試用,可以在。
	
	
本站文章除注明轉(zhuǎn)載外,均為本站原創(chuàng)或翻譯。歡迎任何形式的轉(zhuǎn)載,但請務(wù)必注明出處、不得修改原文相關(guān)鏈接,如果存在內(nèi)容上的異議請郵件反饋至chenjj@ke049m.cn