最近在做一個自動測試的平臺,使用的是Spring的自帶測試庫。如何使用,這裏不再說了,網上有很多,推薦開濤寫的博客
我的主要測試代碼:
//主要構造mock請求類,可以不用看
try {
for(TestClass tmpClass:classes){
List<TestUrl> urls = tmpClass.getUrls();
for(TestUrl url:urls){
String urlString = url.getCompositeUrl();
MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.get(urlString);
initRequestParams(requestBuilder,url);
mockMvc.perform(requestBuilder).andDo(MockMvcResultHandlers.print());
}
}
} catch (Exception e) {
e.printStackTrace();
}
//初始化request參數
private void initRequestParams(MockHttpServletRequestBuilder builder,TestUrl url) {
//設置編碼格式
builder.characterEncoding("utf-8");
TestParameter parameter = url.getParamter();
TestContent content = url.getContent();
TestSession sessionData = url.getSession();
//添加session
if(sessionData != null){
MockHttpSession mockHttpSession = new MockHttpSession();
Map<String, Object> maps = sessionData.getSession();
Set<Map.Entry<String, Object>> sets = maps.entrySet();
for(Map.Entry<String, Object> entry:sets){
mockHttpSession.setAttribute(entry.getKey(), entry.getValue());
}
builder.session(mockHttpSession);
}
if(parameter != null){
Map<String, String> paramMaps = parameter.getParams();
Set<Map.Entry<String, String>> sets = paramMaps.entrySet();
StringBuilder paramBuilder = new StringBuilder();
for(Map.Entry<String, String> entry:sets){
paramBuilder.append('&');
paramBuilder.append(entry.getKey());
paramBuilder.append('=');
paramBuilder.append(entry.getValue());
}
String bodyData = paramBuilder.deleteCharAt(0).toString();
System.out.println(bodyData);
builder.content(bodyData);//這裏是重點
builder.contentType(MediaType.APPLICATION_FORM_URLENCODED);//這裏是重點
// for(Map.Entry<String, String> entry:sets){
// builder.param(entry.getKey(), entry.getValue());
// }
return;
}
}
根據HTTP協議,當body中參數以form表單格式進行提交時,在服務器端通過request.getParameter(“name”)來獲取是可以獲取到的,效果和在Url中使用參數是一樣的。然而當我在這裏設置參數時發現是有問題的,test結果顯示缺少參數,而缺少的參數是在body裏面的,這說明mock並沒有content-type類型去解析content的數據。
於是我查看了Spring中MockHttpServletRequestBuilder的源碼
...
private String contentType;//content類型
private byte[] content;//content數據
private final MultiValueMap<String, String> parameters = new LinkedMultiValueMap<String, String>();//參數列表
....
/**
* Set the request body as a UTF-8 String.
* @param content the body content
*/
public MockHttpServletRequestBuilder content(String content) {
try {
this.content = content.getBytes("UTF-8");
}
catch (UnsupportedEncodingException e) {
// should never happen
}
return this;
}
/**
* Add a request parameter to the {@link MockHttpServletRequest}.
* <p>If called more than once, new values get added to existing ones.
* @param name the parameter name
* @param values one or more values
*/
public MockHttpServletRequestBuilder param(String name, String... values) {
addToMultiValueMap(this.parameters, name, values);//將name和value添加到parameter中,允許一個名字對應多個value值
return this;
}
Ok,以上是初始化的方法。下面是重點,builder的最終目的是build一個MockHttpServletRequest,而這個工作是有buildRequest()方法來完成的。
部分:
try {
if (this.url.getRawQuery() != null) {
request.setQueryString(this.url.getRawQuery());
}
MultiValueMap<String, String> queryParams =
UriComponentsBuilder.fromUri(this.url).build().getQueryParams();
for (Entry<String, List<String>> entry : queryParams.entrySet()) {
for (String value : entry.getValue()) {
value = (value != null) ? UriUtils.decode(value, "UTF-8") : null;
request.addParameter(UriUtils.decode(entry.getKey(), "UTF-8"), value);
}
}
}
catch (UnsupportedEncodingException ex) {
// shouldn't happen
}
for (String name : this.parameters.keySet()) {
for (String value : this.parameters.get(name)) {
request.addParameter(name, value);
}
}
request.setContentType(this.contentType);
request.setContent(this.content);
request.setCharacterEncoding(this.characterEncoding);
由buildRequest的代碼可以發現,Mock將URL的參數和通過使用param添加的參數添加到request中的parameter中了,而將content內容、類型並沒有進行解析,直接添加到request的content中了。
讓我們再查看MockHttpServletRequest的代碼:
@Override
public String getParameter(String name) {
String[] arr = (name != null ? this.parameters.get(name) : null);
return (arr != null && arr.length > 0 ? arr[0] : null);
}
MockHttpServletRequest實現了HttpServletRequest,而在getParameter進行了重寫,即從parameter的map中進行讀取。
這也就解釋了文章開頭提出的問題。