Java Web并發單元測試摘記(1)
大的web項目開發和運行環境都在服務器容器中進行為主,包括調試過程,也都是單例進過Web觸發進行測試。在Web項目中設計使用Spring、hibernate、JBPM工作流、ehcache各種框架或組件。這些東西在一起配置好一個有效的、覆蓋所有環境的單元測試用例也比較復雜。所有配置好一個可用有效的單元測試環境十分必要。并且面對單元測試對并發要求的不支持,還得加入必要的并發測試組件,滿足測試全面系統白盒測試要求。
這里總結幾種有效的配置單元測試方式:
1、直接使用Junit4組件,進行簡單單元測試,在setUp()中初始化各種測試環境如下:
@Override protected void setUp() throws Exception { super.setUp(); String[] paths = { "classpath:applicationContext.xml" }; ApplicationContext ctx = new ClassPathXmlApplicationContext(paths); SpringContextListener.setApplicationContext(ctx); // ############模擬servlet容器啟動,手動更改配置文件路徑 Constant.CURRENT_PROJECT_PATH = "src/"; // ############直接容器中獲取bean userManager = (UserManager) ctx.getBean("userManager"); userService = (UserService) ctx.getBean("userService"); } |
2、使用Sprint-test.jar框架,支持事務可注解方式啟動的單元測試:
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:applicationContext.xml") @TransactionConfiguration(transactionManager="transactionManager",defaultRollback=false) //true將會回滾所有操作,默認沒有該注解為回滾 public class JBPMProcessTest2 extends AbstractTransactionalJUnit4SpringContextTests{ |
以上都只能暫時支持簡單功能的單元測試。但是要測試并發和壓力等。需要加入一個常用的框架,支持在單元測試中注入多線程和并發。
具體參見:http://blog.csdn.net/zhangyaoming2004/article/details/7619489 下面附有Jar包下載 http://yunpan.cn/QUtVay66VnU4T
我們具體使用的功能不是很多,這些并發測試可以嵌入到上面兩種單元測試方式中:如下:
/** * */ package com.dtsz.model.service.test; import net.sourceforge.groboutils.junit.v1.TestRunnable; import com.dtsz.model.entity.report.common.InvokeSource; import com.dtsz.model.service.ExcelReportLogicService; import com.dtsz.model.service.TaskLogicService; import com.dtsz.model.util.ExcelReportUtil; import com.dtsz.view.vo.authenticator.UserVO; import com.dtsz.view.vo.webservice.ExcelReportResult; /** * 模擬插件端數據上報的并發操作測試 * * @author xiaoli * */ public class CreateMulltiTaskReportGroboThread extends TestRunnable { TaskLogicService taskLogicService; ExcelReportLogicService excelReportLogicService; private String reportMap; private UserVO user; private InvokeSource invokeSource; private String bbq; public CreateMulltiTaskReportGroboThread(String reportMap, UserVO user, InvokeSource invokeSource,String bbq) { super(); this.reportMap = reportMap; this.user = user; this.invokeSource = invokeSource; this.bbq = bbq; init(); } public void init() { taskLogicService = (TaskLogicService)ExcelReportUtil.getBean("taskLogicService"); excelReportLogicService = (ExcelReportLogicService)ExcelReportUtil.getBean("excelReportLogicService"); } /* * (non-Javadoc) * * @see net.sourceforge.groboutils.junit.v1.TestRunnable#runTest() */ @Override public void runTest() throws Throwable { // TODO Auto-generated method stub // ########################模擬當前任務該報表期任務上報 //保存數據 ExcelReportResult result = taskLogicService.reportData(reportMap, user, invokeSource); excelReportLogicService.saveData(result); System.out.println("報表期為:"+this.bbq+"已上報"); } } 單元測試并發: /** * */ package com.dtsz.model.service.test; import java.sql.SQLException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import junit.framework.TestCase; import net.sourceforge.groboutils.junit.v1.MultiThreadedTestRunner; import net.sourceforge.groboutils.junit.v1.TestRunnable; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.dtsz.model.entity.authenticator.User; import com.dtsz.model.entity.report.common.InvokeSource; import com.dtsz.model.service.authenticator.UserManager; import com.dtsz.model.service.authenticator.UserService; import com.dtsz.model.service.exception.BaseAppException; import com.dtsz.model.util.BeanUtil2; import com.dtsz.model.util.Constant; import com.dtsz.model.util.Encryption; import com.dtsz.view.util.SpringContextListener; import com.dtsz.view.vo.authenticator.UserVO; |
/** * 模擬數據上報,這里的初始化數據根據插件端傳入數據組合來的,當前只有根據報表期生成初始化數據 * * @author xiaoli * */ public class CreateMultiTaskReportTest3 extends TestCase { private UserManager userManager; private UserService userService; @Override protected void setUp() throws Exception { super.setUp(); String[] paths = { "classpath:applicationContext.xml" }; ApplicationContext ctx = new ClassPathXmlApplicationContext(paths); SpringContextListener.setApplicationContext(ctx); // ############模擬servlet容器啟動,手動更改配置文件路徑 Constant.CURRENT_PROJECT_PATH = "src/"; // ############直接容器中獲取bean userManager = (UserManager) ctx.getBean("userManager"); userService = (UserService) ctx.getBean("userService"); } @Override protected void tearDown() throws Exception { super.tearDown(); } @Test public void test(){ // ###########自動生成報表期串集合 List<String> bbqs = new ArrayList<String>(); UnitTestUtils.imitateCreateMultiBbq(bbqs, 2, "2048", "5"); // 創建線程 TestRunnable[] tr = new TestRunnable[bbqs.size()]; int processCount = 0; // ##########對這些數據進行填報 for (String bbq : bbqs) { // 模擬上報數據 String reportMap = "{taskID:\"402880ee425a92f501425aa75ad50002\",bbq_:\"" + bbq + "\"" + ",bbhid:\"402855b942099b1b014209b1177e03f5\",approvalFlag:2,reportFlag:False,auditFlag:False,sheetMap:" + "[{id:\"402880ee425a92f501425aa75ad60004\",sheetType:\"BASIC\",values:{}}]}"; // 模擬上報的user UserVO user = null; try { user = checkUser("xl1", "1"); } catch (BaseAppException e) { e.printStackTrace(); } // 模擬插件端checkUser String sessionInfo = "{institutionname:\"南昌分行\",institutioncode:\"A01NC\",username:\"肖力1" + "1\",usercode:\"xl1\",rolecode:\"null\",bbhId:\"402855b942099b1b014209b1177e03f5\",bbhcode:\"B01b\",btype:\"BASIC\",taskid:\"402880ee425a92f501425a" + "a75ad50002\",bbhname:\"基層財務b\",bbq:\"" + bbq + "\",frequency:\"MONTH\",SESSIONDIMENSION:{}}"; try { userService.updateSession(sessionInfo, user); } catch (BaseAppException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } // 模擬插件端updateSession // 初始化上報方式 InvokeSource invokeSource = InvokeSource.WSCALL; CreateMulltiTaskReportGroboThread reportThread = new CreateMulltiTaskReportGroboThread(reportMap,user, invokeSource,bbq); tr[processCount] = reportThread; processCount++; } System.out.println("#######################################計時開始########################"); long startTime = System.currentTimeMillis(); // 生成測試線程運行器 MultiThreadedTestRunner mttr = new MultiThreadedTestRunner(tr); // 運行測試線程 try { mttr.runTestRunnables(); } catch (Throwable e) { e.printStackTrace(); } System.out.println("#######################一共耗時:" + String.valueOf((System.currentTimeMillis() - startTime) / 1000) + "秒"); } /** * xl1 1 * */ public UserVO checkUser(String userid, String password) throws BaseAppException { Map<String, String> paramMap = new HashMap<String, String>(); paramMap.put("UserName", userid); paramMap.put("Password", password); List<User> users = userManager.findByCodeCache(userid); List<User> needUsers = new ArrayList<User>(); if (users != null && users.size() > 0) { for (User user : users) { if (user.getState() && user.getCode().equals(userid) && user.getPassword().equals(Encryption.encryption(password))) { needUsers.add(user); } } } // 通過驗證 if (needUsers != null && needUsers.size() > 0) { User user = needUsers.get(0); // 獲取用戶權限,并存放于session中 // UserVO userVO = userManager.getUserPermission(user.getId()); String ipAddress = "0.0.0.0"; UserVO userVO = new UserVO(); BeanUtil2.copyProperties(user, userVO); userVO.setIpAddress(ipAddress); return userVO; // 通過驗證 } else { return null; } } } |
真實環境并發和壓力測試不僅僅是在多個線程同時啟動在測,還可能是增量式壓力測試。這些還是要封裝好和處理好。