SSH框架下單元測(cè)試的實(shí)現(xiàn)
實(shí)現(xiàn)的功能
- 實(shí)現(xiàn)了部門的增刪改查
- 對(duì)Action進(jìn)行了單元測(cè)試
- 對(duì)Service 進(jìn)行了單元測(cè)試,通過(guò)mock的方式實(shí)現(xiàn)。
實(shí)現(xiàn)的步驟
一、對(duì)Action層的單元測(cè)試實(shí)現(xiàn)
1、首先在pom文件中需要引入的依賴
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>1.10.19</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-junit-plugin</artifactId>
<version>2.1.8</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>3.2.8.RELEASE</version>
</dependency>
說(shuō)明:
在此處我們引入struts2-junit-plugin.2.1.8.jar,因?yàn)槠淅锩嬗袦y(cè)試Struts2中action的類
StrutsSpringTestCase,用來(lái)測(cè)試ssh中的action。
2、新建一個(gè)測(cè)試類
如果你使用的是idea,那么你可以直接在需要建立測(cè)試類的Action類中
按ctrl+shift+t,新建一個(gè)測(cè)試類。如DepartmentActionTest
3、如果是普通的action的話,
待測(cè)試代碼:
public String findById() {
String did = ServletActionContext.getRequest().getParameter("did");
department=departmentService.findByDid(Integer.valueOf(did));
return "goEditDepartment";
}
測(cè)試代碼:
@Test
public void testFindById() throws Exception {
/*這個(gè)函數(shù)相當(dāng)@Before注解的函數(shù),是調(diào)用單元測(cè)試后的時(shí)候,
首先會(huì)執(zhí)行的方法。可以在這里面做一些必要的準(zhǔn)備工作*/
request.setParameter("did", "1");
ActionProxy proxy = getActionProxy("/department_findById.action");
DepartmentAction action = (DepartmentAction) proxy.getAction();
String result = action.findById();
Assert.assertEquals("goEditDepartment", result);
}
4、如果是返回json的action的話,待測(cè)試代碼:
public void findAll() {
List<Department> departmentList= departmentService.findAll();
JSONObject jsonObject = new JSONObject();
if (CollectionUtils.isEmpty(departmentList)) {
jsonObject.put("key", "success");
jsonObject.put("data", JSON.toJSONString(departmentList));
writeJson(jsonObject);
return;
}
jsonObject.put("key", "success");
jsonObject.put("data", JSON.toJSONString(departmentList));
writeJson(jsonObject);
}
測(cè)試代碼:
String result = executeAction("/json_findAll.action");
Assert.assertNotNull(result);
JSONObject jsonObject = JSON.parseObject(result);
if ("success".equals(jsonObject.getString("key"))) {
List<Department> departmentList = JSON.parseArray(jsonObject.getString("data"), Department.class);
if (CollectionUtils.isEmpty(departmentList)) {
return;
}
for (Department department : departmentList) {
System.out.println(department.toString());
}
}
5、關(guān)于lazy問(wèn)題的解決:首先在setUp()函數(shù)中加入下述代碼:
@Override
protected void setUp() throws Exception {
super.setUp();
SessionFactory sessionFactory = lookupSessionFactory(request);
Session hibernateSession= getSession(sessionFactory);
TransactionSynchronizationManager.bindResource(sessionFactory,
new SessionHolder(hibernateSession));
//在只讀模式下(FlushMode.NEVER/MANUAL)寫操作不被允許
hibernateSession.setFlushMode(FlushMode.AUTO);
}
然后在添加兩個(gè)私有函數(shù)
private Session getSession(SessionFactory sessionFactory) throws DataAccessResourceFailureException {
Session session = sessionFactory.openSession();
FlushMode flushMode = FlushMode.NEVER;
if (flushMode != null) {
session.setFlushMode(flushMode);
}
return session;
}
private SessionFactory lookupSessionFactory(HttpServletRequest request) {
//“sessionFactory”是你spring配置文件(通常是application.xml)中的SessionFactory。
//如:org.springframework.orm.hibernate4.annotation.AnnotationSessionFactoryBean
return (SessionFactory)this.applicationContext.getBean("sessionFactory");
}
說(shuō)明:通過(guò)lookupSessionFactory()方法獲取這個(gè)測(cè)試環(huán)境中的org.hibernate.SessionFactory實(shí)體對(duì)象,其中
applicationContext是org.springframework.context.ApplicationContext的實(shí)現(xiàn)org.springframework.context.support.GenericApplicationContext
我們可以通過(guò)它的getBean方法來(lái)獲取你配置文件中配置的SessionFactory。使用注解是org.springframework.orm.hibernate4.annotation.AnnotationSessionFactoryBean
不需要使用注解的時(shí)候可以使用org.springframework.orm.hibernate.LocalSessionFactoryBean來(lái)實(shí)現(xiàn)。
該單元測(cè)試需要加載spring配置文件信息,默認(rèn)加載路徑是你項(xiàng)目的src目錄下,文件名默認(rèn)為applicationContext.xml,如果路徑不對(duì)或者
文件名不同,則需要重寫getContextLocations()方法,如
protected String getContextLocations() {
return "classpath*:applicationContext.xml";
}
關(guān)于實(shí)現(xiàn)web session的問(wèn)題,很簡(jiǎn)單,該類提供了一個(gè)MockHttpServletRequest成員變量,我們只要mock一個(gè)session出來(lái),然后加入到這個(gè)request中,就可以實(shí)現(xiàn)session的模擬了。示例代碼如下:
HttpSession session = new MockHttpSession();
String sessionId = UUID.randomUUID().toString();
session.setAttribute(ConstParameter.USER_SESSION, sessionId);
//user是一個(gè)用戶信息的類,你可以根據(jù)你的需要自己定義
UserInfor user = new UserInfo();
user.setUserId(1);
user.setName("xxx");
session.setAttribute(ConstParameter.USER_INFO, user);
request.setSession(session);
關(guān)于action的單元測(cè)試我們就說(shuō)完了。
二、Service的單元測(cè)試
接下來(lái)我們?cè)谡f(shuō)說(shuō)關(guān)于service的測(cè)試,
同樣,我們先從依賴說(shuō)起,需要添加的依賴:
<powermock.version>1.7.1</powermock.version>
<!--********************powermock使用*********************-->
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
此處我們采用的是powermock+junit 進(jìn)行mock測(cè)試。為啥使用powermock呢,毋庸置疑,由于他的功能比較強(qiáng)大
1、首先,我們我們看看待測(cè)試的代碼:
@Override
public List<MyInvoice> getMyInvoice(String buyerId) {
if (StringUtils.isBlank(buyerId)) {
return null;
}
List<MyInvoice> myInvoices = new ArrayList<MyInvoice>();
String url = baseDataUrl + UrlConfig.GET_MYINVOICE_URL + "?t=" + VerifyBaseUtil.getT()
+ "&token=" + VerifyBaseUtil.getToken() + "&buyerId=" + buyerId;
System.out.println("MyInvoiceServiceImpl getMyInvoice接口請(qǐng)求參數(shù)為:" + url);
try {
String responseInfo = HttpUtil.getHttp(url);
System.out.println("MyInvoiceServiceImpl getMyInvoice接口返回結(jié)果為:" + responseInfo);
Map<String, Object> result = JSON.parseObject(responseInfo, Map.class);
if (DistrictReturnNum.SUCCESS.getValue().equals(result.get("code"))) {
myInvoices = JSON.parseArray(JSON.toJSONString(result.get("result")), MyInvoice.class);
return myInvoices;
}
} catch (Exception e) {
System.out.println("MyInvoiceServiceImpl getMyInvoice 程序出錯(cuò),查詢發(fā)票失敗"+e.getMessage());
return null;
}
return null;
}
getMyInvoice方法是一個(gè)調(diào)用外部接口的方法,通過(guò)http協(xié)議進(jìn)行通信。這兒有兩個(gè)問(wèn)題
1.HttpUtil.getHttp(url) 是一個(gè)靜態(tài)方法,我們?nèi)绾蝝ock?
2.我們?nèi)绾蝝ock這個(gè)方法所在的實(shí)現(xiàn)類?因?yàn)樵搶?shí)現(xiàn)類是通過(guò)spring ioc 容器生成并注入的。
要回答這兩個(gè)問(wèn)題,我們首先需要看看,我們的測(cè)試代碼:
@RunWith(PowerMockRunner.class)
@PrepareForTest(HttpUtil.class)
public class MyInvoiceServiceImplTest {
@InjectMocks
private MyInvoiceService myInvoiceService = new MyInvoiceServiceImpl();
@Before
public void setUp(){
PowerMockito.mockStatic(HttpUtil.class);
}
@Test
public void testGetMyInvoice() throws Exception {
String result_http="{\"result\":[{\"addDate\":1509010776000,\"buyerId\":" +
"\"9E59A2D27B7748848FB65041B854240E\",\"headName\":\"項(xiàng)偉測(cè)試\"," +
"\"headType\":\"0\",\"invoiceId\":\"9747A51B57FF4EA781F1CFDF73A0D9DF\"," +
"\"invoiceType\":\"0\",\"isDefault\":0},{\"addDate\":1509092635000,\"" +
"buyerId\":\"9E59A2D27B7748848FB65041B854240E\",\"editDate\":1509094177000,\"headName\":\"項(xiàng)偉測(cè)試二\",\"headType\":\"0\",\"invoiceId\":\"720CF6C50E594283B01C79D03D6D52B2\"" +
",\"invoiceType\":\"0\",\"isDefault\":1}],\"msg\":\"成功\",\"code\":104}";
// 1、 buyerId為空
String buyerId = null;
Assert.assertEquals(null, myInvoiceService.getMyInvoice(buyerId));
// 2、buyerId不為空
buyerId = "FF8080810F5E601526";
PowerMockito.when(HttpUtil.getHttp(anyString())).thenReturn(result_http);
List<MyInvoice> result = myInvoiceService.getMyInvoice(buyerId);
Assert.assertEquals(2,result.size());
}
}
第一個(gè)問(wèn)題:我們通過(guò)PowerMockito.mockStatic(HttpUtil.class); 一個(gè)靜態(tài)方法的實(shí)現(xiàn)類。然后就可以調(diào)用該靜態(tài)方法。
第二個(gè)問(wèn)題:@InjectMocks注解來(lái)mock我們需要測(cè)試的業(yè)務(wù)類。
至此,我們就可以通過(guò)powermock對(duì)service層的方法進(jìn)行單元測(cè)試了。
運(yùn)行環(huán)境
- 環(huán)境描述:Struts2+Spring4.2.4+hibernate4
JAVA 1.7 - 額外依賴第三方j(luò)ar包,請(qǐng)參考pom.xml
作者:碼農(nóng)飛哥
微信公眾號(hào):碼農(nóng)飛哥