Strut2中单元测试实例
項目文件結構圖:
橢圓框中的Jar 包是單元測試時候需要引入的。
矩形框 MainTest 每個包下一個,為 JUnit4 的 Suite 套件,其作用是執行本包下的“測試類”和子包的 MainTest。
例如:jp.co.snjp.ht.MainTest
package jp.co.snjp.ht;import org.junit.runner.RunWith; import org.junit.runners.Suite;@RunWith( Suite.class ) @Suite.SuiteClasses({ jp.co.snjp.ht.orderCheck.MainTest.class, jp.co.snjp.ht.outPreconcert.MainTest.class,jp.co.snjp.ht.partOut.MainTest.class,jp.co.snjp.ht.productCheck.MainTest.class, }) public class MainTest { }由于 jp.co.snjp.ht 包下沒有“測試類”,因而只需要引入“子包”的 MainTest 即可!而,jp.co.snjp.ht.orderCheck.MainTest
package jp.co.snjp.ht.orderCheck;import org.junit.runner.RunWith; import org.junit.runners.Suite;@RunWith( Suite.class ) @Suite.SuiteClasses({ CheckBarcodeTest.class, OrderConfirmTest.class }) public class MainTest {}由于 jp.co.snjp.ht.orderCheck 下沒有“子包”,因而只需要引入“測試類”————————————————————————————————
Strut2 提供了隔離容器對象的方法,因而在所有Action 的基類將其織入。
因為本項目很小,沒有單獨的Business 層和DAO 層,業務邏輯在 Action 中完成,SQL 操作在 SqlHelper 中完成。
為了實現單元測試隔離測試效果,這里提供了 setSqlHelper( SqlHelper sqlHelper ) 方法,這樣就可以傳入模擬的 SqlHelper 對象
package jp.co.snjp.ht.util;import java.util.Date; import java.util.List; import java.util.Map;import jp.co.snjp.dao.SqlHelper;import org.apache.struts2.interceptor.CookiesAware; import org.apache.struts2.interceptor.RequestAware; import org.apache.struts2.interceptor.SessionAware;import com.opensymphony.xwork2.ActionSupport;public class BaseAction extends ActionSupport implements RequestAware,SessionAware,CookiesAware{private static final long serialVersionUID = 1L;protected Map<String,Object> requestMap;protected Map<String,Object> sessionMap;protected Map<String,String> cookieMap;/*** 查詢結果集*/protected List<Object> list;/*** SQL 執行幫助類*/protected SqlHelper sqlHelper;public void setRequest(Map<String, Object> requestMap) {this.requestMap = requestMap;}public void setSession(Map<String, Object> sessionMap) {this.sessionMap = sessionMap;}public void setCookiesMap(Map<String, String> cookieMap) {this.cookieMap = cookieMap;}public void setSqlHelper( SqlHelper sqlHelper ){this.sqlHelper = sqlHelper;}/*** 記錄存儲過程執行的日志信息* @param start* @param name** Date :2012-6-7* Author :GongQiang* @throws Exception */protected void logStroeProcedure( Date start, String name ) throws Exception{String formatDateTime = Utils.formatDateTime( start );String userId = (String) sessionMap.get( "user_id" );String sql = "insert into HT_CCGC_LOG(usercode,ccgcmc,kszxsj) "+" values ('"+ userId +"','"+ name +"','"+ formatDateTime+"' );";sqlHelper.executeSQL( sql );} }雖然提供了?setSqlHelper( SqlHelper sqlHelper ) 方法,但是這方法在什么時候調用呢?為了解決這個問題,就只能把實際的業務邏輯放到 doExecute() 方法下去執行,而在 execute()方法下調用 setSqlHelper()方法,只用測試 doExecute()方法。
doExecute()方法修飾為包可見,這樣就只有測試代碼可以訪問。代碼如下:
package jp.co.snjp.ht.productCheck;import java.math.BigDecimal; import java.util.List; import java.util.Map;import jp.co.snjp.dao.SqlHelper; import jp.co.snjp.ht.util.BaseAction; import jp.co.snjp.ht.util.SpotTicketBarcodeParser; /*** 部品檢查錄入-Barcode掃描Action* @author GongQiang**/ public class ProductCheckBarcode extends BaseAction {private static final long serialVersionUID = 1L;private String barcode;private String backUrl;public String getBarcode() {return barcode;}public void setBarcode(String barcode) {this.barcode = barcode;}public String getBackUrl() {return backUrl;}public void setBackUrl(String backUrl) {this.backUrl = backUrl;}/*** error_0 條碼不符合規則* error_1 訂單在DB中不存在 或 訂單已經執行完畢* error_2 訂單區分錯誤*/@Overridepublic String execute() throws Exception {super.execute();setBackUrl( "productCheck/scanBarcode.jsp" );setSqlHelper( new SqlHelper() );return doExecute();}String doExecute()throws Exception {SpotTicketBarcodeParser parser = new SpotTicketBarcodeParser( barcode );if( ! parser.valid() ){return "error_0";}queryOrderInfo( parser.getOrderNo() );if( orderNotExist() || orderFinished() ){return "error_1";}if( !checkDistinguish() ){return "error_2";}sessionMap.put( "order_info", list.get(0) );return SUCCESS;}void queryOrderInfo( String orderNo ) throws Exception{String sql = "select top 1 * from iOrder_Check where " +" OrderNo='" + orderNo + "' ;";list = sqlHelper.executeQuery( sql );if( list == null || list.isEmpty() ){return;}queryNameCount(orderNo);}/*** 查詢訂單名稱 和 訂單殘&實收數量* ** Date :2012-6-8* Author :GongQiang* @throws Exception */private void queryNameCount( String orderNo ) throws Exception{String sql = "select sum(nqty) as usedCount from iOrder_Check "+" where orderno='" + orderNo + "' group by orderno;";List usedCountResult = sqlHelper.executeQuery( sql );BigDecimal orderCount = (BigDecimal) ((Map)list.get(0)).get( "pqty" );BigDecimal usedCount = (BigDecimal) ((Map)usedCountResult.get(0)).get( "usedcount" );BigDecimal remainCount = orderCount.subtract( usedCount );sql = "select itemname from iorder_operate where " +" OrderNo='" + orderNo + "' ;";List itemNameResult = sqlHelper.executeQuery( sql );String itemName = (String) ((Map)itemNameResult.get(0)).get( "itemname" );((Map)list.get(0)).put( "remaincount", remainCount );((Map)list.get(0)).put( "usedcount", usedCount );((Map)list.get(0)).put( "itemname", itemName );}/*** DB中沒有關聯的訂單* @return** Date :2012-6-7* Author :GongQiang*/boolean orderNotExist(){if( list == null || list.isEmpty() ){return true;}return false;}/*** 該訂單已經執行完畢* @return** Date :2012-6-7* Author :GongQiang*/boolean orderFinished(){BigDecimal remainCountInOrder = (BigDecimal)((Map)list.get(0)).get( "remaincount" );if( remainCountInOrder != null ){return remainCountInOrder.compareTo( new BigDecimal("0") ) <= 0 ;}return false;}/*** 檢查區分是否正確* @return** Date :2012-6-7* Author :GongQiang*/private boolean checkDistinguish(){String[] rights = { "保證" };String dist = (String) ((Map)list.get(0)).get( "chkdistinguish" );for( int i=0 ; i<rights.length ; i++ ){if( rights[i].equals( dist ) ){return true;}}return false;} }邏輯很簡單,這里僅僅測試最基本的4 條執行路徑
1、條碼解析錯誤
2、訂單在DB中不存在
3、訂單已經執行完成
4、分區錯誤
5、OK
下面是完整的測試類:
package jp.co.snjp.ht.productCheck;import static org.junit.Assert.*;import java.math.BigDecimal; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map;import jp.co.snjp.dao.SqlHelper;import org.easymock.classextension.EasyMock; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test;public class ProductCheckBarcodeTest {@BeforeClasspublic static void setUpBeforeClass() throws Exception {}@AfterClasspublic static void tearDownAfterClass() throws Exception {}/*** 錯誤條碼* ** Date :2012-6-18* Author :GongQiang* @throws Exception */@Testpublic void testDoExecute_errorBarcode() throws Exception {ProductCheckBarcode action = new ProductCheckBarcode();action.setBarcode( "xxx0001" );assertEquals("error_0", action.doExecute() );action = new ProductCheckBarcode();action.setBarcode( "0123456789012345678901234567890123456789555" );assertEquals("error_0", action.doExecute() );}/*** DB中沒有關聯的記錄* ** Date :2012-6-18* Author :GongQiang* @throws Exception */@Testpublic void testDoExecute_noRecord() throws Exception {SqlHelper mockSqlHelper = EasyMock.createMock( SqlHelper.class );// 返回結果EasyMock.expect( mockSqlHelper.executeQuery( (String)EasyMock.anyObject() )).andReturn( new ArrayList<Map<String,Object>>() );// ReplayEasyMock.replay( mockSqlHelper );ProductCheckBarcode action = new ProductCheckBarcode();action.setBarcode( "xxx0001|bbb" );action.setSqlHelper( mockSqlHelper );assertEquals("error_1", action.doExecute() );//VerifyEasyMock.verify( mockSqlHelper );//-----------------------------------------mockSqlHelper = EasyMock.createMock( SqlHelper.class );// 返回結果EasyMock.expect( mockSqlHelper.executeQuery( (String)EasyMock.anyObject() )).andReturn( null );// ReplayEasyMock.replay( mockSqlHelper );action = new ProductCheckBarcode();action.setBarcode( "xxx0001|bbb" );action.setSqlHelper( mockSqlHelper );assertEquals("error_1", action.doExecute() );//VerifyEasyMock.verify( mockSqlHelper );}/*** 記錄已經執行完畢* ** Date :2012-6-18* Author :GongQiang* @throws Exception */@Testpublic void testDoExecute_finished() throws Exception {//list -- 返回的結果集Map<String, Object> map = new HashMap<String,Object>();map.put("pqty", new BigDecimal("100")); //訂單關聯數量map.put("usedcount", new BigDecimal("100")); //已經使用數量 -->剩余數量就是0map.put("itemname", "TextOrderXXX");List<Map<String,Object>> list = new ArrayList<Map<String,Object>>();list.add( map );SqlHelper mockSqlHelper = EasyMock.createMock( SqlHelper.class );EasyMock.expect( mockSqlHelper.executeQuery( (String)EasyMock.anyObject() )).andReturn( list ).times(3);// ReplayEasyMock.replay( mockSqlHelper );ProductCheckBarcode action = new ProductCheckBarcode();action.setBarcode( "xxx0001|bbb" );action.setSqlHelper( mockSqlHelper );assertEquals("error_1", action.doExecute() );//VerifyEasyMock.verify( mockSqlHelper );}/*** 錯誤的分區* ** Date :2012-6-18* Author :GongQiang* @throws Exception */@Testpublic void testDoExecute_errorDistinguish() throws Exception {//list -- 返回的結果集Map<String, Object> map = new HashMap<String,Object>();map.put("pqty", new BigDecimal("100")); //訂單關聯數量map.put("usedcount", new BigDecimal("50")); //已經使用數量 -->剩余數量就是50map.put("itemname", "TextOrderXXX");map.put( "chkdistinguish", "不存在" );List<Map<String,Object>> list = new ArrayList<Map<String,Object>>();list.add( map );SqlHelper mockSqlHelper = EasyMock.createMock( SqlHelper.class );EasyMock.expect( mockSqlHelper.executeQuery( (String)EasyMock.anyObject() )).andReturn( list ).times(3);// ReplayEasyMock.replay( mockSqlHelper );ProductCheckBarcode action = new ProductCheckBarcode();action.setBarcode( "xxx0001|bbb" );action.setSqlHelper( mockSqlHelper );assertEquals("error_2", action.doExecute() );//VerifyEasyMock.verify( mockSqlHelper );}/*** 正常* ** Date :2012-6-18* Author :GongQiang* @throws Exception */@Testpublic void testDoExecute_ok() throws Exception {//list -- 返回的結果集Map<String, Object> map = new HashMap<String,Object>();map.put("pqty", new BigDecimal("100")); //訂單關聯數量map.put("usedcount", new BigDecimal("50")); //已經使用數量 -->剩余數量就是50map.put("itemname", "TextOrderXXX");map.put( "chkdistinguish", "保證" );List<Map<String,Object>> list = new ArrayList<Map<String,Object>>();list.add( map );SqlHelper mockSqlHelper = EasyMock.createMock( SqlHelper.class );EasyMock.expect( mockSqlHelper.executeQuery( (String)EasyMock.anyObject() )).andReturn( list ).times(3);// ReplayEasyMock.replay( mockSqlHelper );ProductCheckBarcode action = new ProductCheckBarcode();action.setBarcode( "xxx0001|bbb" );action.setSqlHelper( mockSqlHelper );action.setSession( new HashMap<String,Object>() );assertEquals("success", action.doExecute() );//VerifyEasyMock.verify( mockSqlHelper );} }下面詳細講解測試方法的寫法:1、條碼解析錯誤
/*** 錯誤條碼* ** Date :2012-6-18* Author :GongQiang* @throws Exception */@Testpublic void testDoExecute_errorBarcode() throws Exception {ProductCheckBarcode action = new ProductCheckBarcode();action.setBarcode( "xxx0001" );assertEquals("error_0", action.doExecute() );action = new ProductCheckBarcode();action.setBarcode( "0123456789012345678901234567890123456789555" );assertEquals("error_0", action.doExecute() );}當條碼解析錯誤時,查詢沒有機會執行也就沒有必要傳入 SqlHelper 對象。
2、訂單在DB中不存在
注意:模擬方法執行時候是嚴格的參數匹配的,為簡易性這里直接使用 EasyMock.anyObject(),這樣任何參數都能匹配執行。
3、訂單已經執行完成
/*** 記錄已經執行完畢* ** Date :2012-6-18* Author :GongQiang* @throws Exception */@Testpublic void testDoExecute_finished() throws Exception {//list -- 返回的結果集Map<String, Object> map = new HashMap<String,Object>();map.put("pqty", new BigDecimal("100")); //訂單關聯數量map.put("usedcount", new BigDecimal("100")); //已經使用數量 -->剩余數量就是0map.put("itemname", "TextOrderXXX");List<Map<String,Object>> list = new ArrayList<Map<String,Object>>();list.add( map );SqlHelper mockSqlHelper = EasyMock.createMock( SqlHelper.class );EasyMock.expect( mockSqlHelper.executeQuery( (String)EasyMock.anyObject() )).andReturn( list ).times(3);// ReplayEasyMock.replay( mockSqlHelper );ProductCheckBarcode action = new ProductCheckBarcode();action.setBarcode( "xxx0001|bbb" );action.setSqlHelper( mockSqlHelper );assertEquals("error_1", action.doExecute() );//VerifyEasyMock.verify( mockSqlHelper );}在實際代碼中,當查詢到記錄時就要繼續兩個 SQL查詢操作(1、查詢訂單名稱;2、查詢訂單關聯數量和已經檢查數量)。并依次往 list 結果集中添加對象,但是在測試中為了方便起見,直接 一次性構造出完整的結果并 重復執行 3次。
4、分區錯誤
/*** 錯誤的分區* ** Date :2012-6-18* Author :GongQiang* @throws Exception */@Testpublic void testDoExecute_errorDistinguish() throws Exception {//list -- 返回的結果集Map<String, Object> map = new HashMap<String,Object>();map.put("pqty", new BigDecimal("100")); //訂單關聯數量map.put("usedcount", new BigDecimal("50")); //已經使用數量 -->剩余數量就是50map.put("itemname", "TextOrderXXX");map.put( "chkdistinguish", "不存在" );List<Map<String,Object>> list = new ArrayList<Map<String,Object>>();list.add( map );SqlHelper mockSqlHelper = EasyMock.createMock( SqlHelper.class );EasyMock.expect( mockSqlHelper.executeQuery( (String)EasyMock.anyObject() )).andReturn( list ).times(3);// ReplayEasyMock.replay( mockSqlHelper );ProductCheckBarcode action = new ProductCheckBarcode();action.setBarcode( "xxx0001|bbb" );action.setSqlHelper( mockSqlHelper );assertEquals("error_2", action.doExecute() );//VerifyEasyMock.verify( mockSqlHelper );}這里就是注意構造參數,使得前面的判斷都成功,到這里判斷分區時錯誤。
5、OK
/*** 正常* ** Date :2012-6-18* Author :GongQiang* @throws Exception */@Testpublic void testDoExecute_ok() throws Exception {//list -- 返回的結果集Map<String, Object> map = new HashMap<String,Object>();map.put("pqty", new BigDecimal("100")); //訂單關聯數量map.put("usedcount", new BigDecimal("50")); //已經使用數量 -->剩余數量就是50map.put("itemname", "TextOrderXXX");map.put( "chkdistinguish", "保證" );List<Map<String,Object>> list = new ArrayList<Map<String,Object>>();list.add( map );SqlHelper mockSqlHelper = EasyMock.createMock( SqlHelper.class );EasyMock.expect( mockSqlHelper.executeQuery( (String)EasyMock.anyObject() )).andReturn( list ).times(3);// ReplayEasyMock.replay( mockSqlHelper );ProductCheckBarcode action = new ProductCheckBarcode();action.setBarcode( "xxx0001|bbb" );action.setSqlHelper( mockSqlHelper );action.setSession( new HashMap<String,Object>() );assertEquals("success", action.doExecute() );//VerifyEasyMock.verify( mockSqlHelper );}這里要注意,因為實際代碼中 調用了sessionMap 的put 方法,因而這里就要傳入一個對象。————————————————————————————————————
擴展:當有單獨的 Business 層和 DAO 層時候。也許沒有辦法像 SqlHelper 簡單的只需要一個接口方法即可,也許就要每個子 Action 設置相應的Business 對象。
總結
以上是生活随笔為你收集整理的Strut2中单元测试实例的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JMS之企业消息传送
- 下一篇: Tomcat学习笔记(一)