天天看點

Junit4單元測試與mockmvc使用

上一篇部落格已經完成了spring4.3與junit4.12的整合,如有疑問歡迎一起探讨學習。接下來将展示單元測試及mockmvc使用。

1、Controller層,在這也是我需要測試的層

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping(value="/")
public class DmController {

	@Autowired
	private DmService dmService; //服務接口
	//儲存對象
	@RequestMapping(value="newdm.do",method = RequestMethod.POST)
	@ResponseBody
	public Object newdm(HttpSession session, DmDTO dmDTO) {  
		User user = getUser(session);
		String name = user.getName();
		if(name != null) {
			String result = dmService.save(dmDTO);
			return Message(String); //Message用于向前端傳回資料對象
		}
		return null;
	}
	
	//get請求,用作頁面跳轉
	@RequestMapping(value="returnjsp.do",method=RequestMethod.GET)
	public String turnjsp(String id) {
		if(id.equals("one")) {
			return "/hello/one";
		}else {
			return "/hello/other";
		}		  
	}
}
           

DmService接口

public interface DmService {

	String save(DmDTO dmDTO);
}
           

DmServiceImpl實作類

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service  //要記得加上這個注解,否則無法注入容器
public class DmServiceImpl implements DmService{

	@Autowired
	private DmDAO dmDAO;
	@Override
	public String save(DmDTO dmDTO) {
		if(dmDTO != null) {
			String result = dmDAO.save(dmDTO);
			return result;
		}
		return null;
	}
}
           

DmDAO接口

public interface DmDAO {

	String save(DmDTO dmDTO);
}
           

DmDAOImpl實作類

@Repository  //切記要加上該注解
public class DmDAOImpl implements DmDAO{
//真實的DAO實作類中應該是與資料庫互動,這裡為了友善就不寫了
	@Override
	public String save(DmDTO dmDTO) {
		@Override
	public String save(DmDTO dmDTO) {
		if(dmDTO.getName().length()<5) {
			return "儲存成功";
		}else {
			return "儲存失敗";
		}
	}
}
           

單元測試案例:

package cn.wolfcode;

import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.request.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.request.MockMvcResultHandlers.status;
import static org.springframework.test.web.servlet.request.MockMvcResultHandlers.view;

//測試類,需要繼承前面整合好的BaseJunit
public class DmControllerTest extends BaseJunit{

	@Autowired
	DmController dmController;  //這裡是直接從容器中拿出DmController這個bean,這個bean裡使用到的依賴,比如DmService都
	@Test                       //已經注入到這個bean裡,是以下面可以直接用這個bean來調用newdm方法進行測試
	public void testNewdm() { //注意:測試方法不能有傳回值,也不能是有參函數
		DmDTO dmDTO =  new DmDTO();
		dmDTO.setName("hao"); //設定長度小于5,應該傳回儲存成功
		Message result =(Message) dmController.newdm(session,dmDTO); //這裡使用到的session在BaseJunit中已經使用mockmvc構造了
		assertEquals("儲存成功",result.getinfo());  //傳回的對象擷取裡面的資訊
	}
	
	//使用mockmc來測試,mockmvc主要是模拟頁面對背景的通路,個人用的不是很習慣
	
	@Test                       
	public void testNewdmWithMockmvc() {
		DmDTO dmDTO =  new DmDTO();
		dmDTO.setName("hao"); 
		String requestJson = JSONObject.toJSONString(dmDTO);//mockmvc隻能傳string類型,對于這種對象類型的需要轉成String類型
		mockMvc.perform(post("newdm.do")  //這裡的mockMvc在BaseJunit已經定義好,請求url及請求方式,如果被測方法是get,這裡對應的換成get即可
				.contentType(MediaType.APPLICATION_JSON) //接受的資料類型
				.content(requestJson)  //請求的資料内容
				.andExpect(status().isOk())  //斷言傳回的狀态是正确的
				.session(session)    //session的方式,括号内的session也是BaseJunit裡構造好的,可以根據自己的要求構造
				).andDo(print())   //結果處理器,用于在控台上列印結果  注意這裡是需要靜态引入
		         .andReturn();
	}
	
	@Test                       
	public void testTurnjsp() {
		mockMvc.perform(get("returnjsp.do"))  //使用get請求
		       .andExpect(status().isOk())
		       .andExpect(view().name("/hello/one")) //使用斷言判斷傳回結果
			   .andDO(print())
			   .andReturn();
	}
}

           

這裡也把之前的BaseJunit放出來吧

import javax.sql.DataSource;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.mock.jndi.SimpleNamingContextBuilder;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.ModelMap;
import org.springframework.web.context.WebApplicationContext;

@RunWith(SpringJUnit4ClassRunner.class) //指定運作的測試類  該類在spring-test裡,有時無法引入隻能手工敲了
@ContextConfiguration(locations= {"classpath*:/applicationContext.xml"})//spring-test裡的類,這個很重要,把配置檔案裡的内容加載到容器中
@WebAppConfiguration

@Transactional(transactionManager="transactionMananger") //這裡的事務即是取applicationContext.xml的事務
@Rollback //預設為true 即事務復原
public class BaseJunit {
	
	@Autowired
	protected WebApplicationContext wac;
	protected MockMvc mockMvc;
	protected MockHttpSession session;
	protected MockHttpServletRequest request;
	protected ModelMap map;

	@BeforeClass //Junit裡的方法,在整個單元測試運作之前先運作,是以可以用于加載xml等檔案操作,這裡加載jndi.xml,
	public static void beforeClass() throws Exception{  //這樣就解決了JNDI連接配接方式不啟動tomcat無法連接配接資料庫的問題
		ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("classpath*:/jndi.xml");//加一個*會整個項目去找這個xml
		DataSource ds = (DataSource) app.getBean("dataSource"); //我們jndi.xml裡配置的資料源id="dataSource"
		SimpleNamingContextBuilder builder = new SimpleNamingContextBuilder();
		builder.bind("java:com/env/jdbc/hello",ds);//資料源與applicationContext.xml裡的jndiName的value綁定
		builder.activate();
	}
	
	@Before //每個測試用例執行前都會執行一次
	public void setup() { //這裡使用MockMvc構造各種session等,這個看個人需要吧
		this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
		this.session = new MockHttpSession();
		this.request = new MockHttpServletRequest();
		this.map = new ModelMap();
		User us = new User();     //使用者實體類
		us.setUmname("lixiaolong");
		session.setAttribute("user",us);
	}
}
           

總結:

由于我們使用的是存儲過程,存儲過程裡有commit,是以單元測試執行存儲過程後是無法事務復原的,這個問題我們後面會使用強大的Jmockit來完成。時間問題,後面再來補充經常遇到的報錯問題,重點是後面的Jmockit強大的功能使用。

常見問題:1、org.springframework.beans.factory.NoSuchBeanDefinitionException:No bean named ‘dataSource’ is defind…

主要是沒有加載到資料源,可以檢視資料庫配置是否有問題以及加載的jndi.xm路徑是否正确了

2、java.lang.IllegalStateException:Failed to load ApplicationContext…

Caused by:org.springframework.beans.factory.UnsatisfiedDependencyException:Error creating bean with name ‘*’:Unsatisfied dependency expressed field ‘’; …

沒加@Service/@Repository/@Controller這些注解的話就會報建立bean錯誤的異常。

3、java.lang.IllegalStateException:Failed to load ApplicationContext…

Caused by:java.lang.IllegalStateException:WebApplicationObjectSupport instance[ResourceHttpRequestHandler[locations=[class path resource[easyui/]],resolvers=…]]…

在測試類上加上@WebAppConfiguration注解即可,如BaseJunit裡就加了該注解。

繼續閱讀