Url接口测试:HttpClient模拟登录效果
最近做的一个需求,用JUnit做业务测试,需要模拟用户登录,尝试了几种方法,总结一下。
HttpUrlConnection(不可行)
SpringFramework.test(部分可行)
HttpClient(可行)
(一)HttpUrlConnection
一开始很自然想到用HttpUrlConnection模拟登录。
public class RequestTest{
private String SessionId;
@Test
public void test1() throws Exception{
login(user, psw); // login.
String result = post(url, [{...},{...}]) // do test.
}
public void longin(String user, String psw) throws Exception{
String strUrl = "...";
Url url = new Url(strUrl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
... // set other params;
conn.setDoInput(true);
conn.setDoOutput(true);
conn.connect();
String cookie = conn.getHeaderField("Set-Cookie");
SessionId = ((cookie.split(";"))[0].split("="))[1];
System.out.println("SessionId = " + SessionId);
}
public String post(String strUrl, String jsonParams) throws Exception{
String result = "";
BufferedReader in = null;
PrintWriter out = null;
try{
URL url = new Url(strUrl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
... // set other params;
conn.setDoInput(true);
conn.setDoOutput(true);
conn.setRequestProperty("Cookie", "SessionId=" + SessionId); // set cookie.
conn.connect();
out = new PrintWriter(conn.getOutputStream());
out.print(jsonParams);
out.flush();
in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line = null;
while((line = in.readLine()) != null){
result = result + line;
}
}catch(Exception e){
throw e;
}finally{
... // close stream.
retrun result;
}
}
}
这里的关键在于登录时通过“Set-Cookie”响应头获取SessionId,然后在后续测试时发送该SessionId到服务器以验证登录权限。但是,实际操作之后发现没有效果,原因在于登录通过之后,由于实际上是没有浏览器在连接的,所以服务器的安全系统认为“浏览器已经关闭”了,所以立即将Session销毁,SessionId失效。于是尝试第二种方法:SpringFramework的测试包。
(二)SpringFramework.test
这实际上是一个偷懒的做法,绕开url请求,通过注解技术直接调用Controller里面相应url所映射的方法。
@ContextConfiguration(locations = {"classpath:mvc-dispatcher-servlet.xml"})
@RunWith(SpringJUnit4ClassRunner.class)
public class RequestTest{
@Autowired
public TestController controller;
@Test
public void test1(){
controller.test()... // do test.
}
}
这种方法逻辑比较简单,代码也少,需要注意的是相关的controller、dao等对象需要通过@Autowired注解创建,不能new。
这种方法在大部分情况下是可行的,包括部分需要登录权限的地方也可以通过验证。但是,当测试到一个方法需要从Session中获取当前登录user的时候,由于没有实际登录,所以获取的user为null,产生了NullPointerException。于是乎,再尝试第三种方法:Apache的HttpClient。
(三)HttpClient
Apache的HttpClient包,测试url接口的神器,经测试可以完美模拟浏览器登录效果。
public class RequestTest{
private static CloseableHttpClient client;
@Autowired
public TestController controller;
@BeforeClass
public static void beforeClass(){
login(user, psw, loginUrl);
}
@AfterClass
public static void beforeClass(){
logout(logoutUrl);
}
@Test
public void test1(){
post(url1);
get(url2);
... // do test.
}
private static boolean login(String user, String psw, String loginUrl){
client = HttpClientBuilder.create().build();
HttpPost post = new HttpPost(loginUrl);
HttpEntity loginEntity;
try{
loginEntity = new UrlEncodedFormEntity(Arrays.asList(new NameValuePair[]{
new BasicNameValuePair("user", user),
new BasicNameValuePair("psw", psw),
}));
post.setEntity(loginEntity);
CloseableHttpResponse response = client.execute(post);
StatusLine status = response.getStatusLine();
if(status.getStatusCode() == 200){
return true;
}
}catch(Exception e){
return false;
}
return false;
}
private static void logout(String logoutUrl){
HttpGet get = new HttpGet(logoutUrl);
try{
CloseableHttpResponse response = client.execute(get);
}catch{
}
}
private String post(String url, Map<String, Object> params) throw ClientProtocolException, IOException{
HttpPost post = new HttpPost(url);
post.setHeadr("Content-type", "application/json"); // send json message to server.
post.setHeadr("Accept", "application/json"); // receive json message from server.
... // set headers and params.
String strParams = new ObjectMapper().writeValueAsString(params);
post.setEntity(new StringEntity(strParams));
CloseableHttpResponse response = client.execute(post);
if(200 == response.getStatusLine().getStatusCode()){
InputStream in = response.getEntity().getContent();
String result = toString(in) // use other API to get String from the input stream.
return result;
}
return null;
}
private String get(String url) throw ClientProtocolException, IOException{
HttpGet get = new HttpGet(url);
... // set headers and params.
CloseableHttpResponse response = client.execute(get);
if(200 == response.getStatusLine().getStatusCode()){
InputStream in = response.getEntity().getContent();
String result = toString(in) // use other API to get String from the input stream.
return result;
}
return null;
}
}
模拟过程:创建一个CloseableHttpClient类型的成员变量client,用client给服务器发送登录请求,登录通过后就模拟了一个“保持连接状态的浏览器”,然后就可以通过client给服务器发送各种请求,效果和通过浏览器发送请求相同。
还有一点需要注意:由于“浏览器”是一直“连着”的,意味着用户一直处于登录状态(直至Session失效),在服务器安全系统拒绝重复登录的情况下,同一用户不能再次登录。有时候我们可能需要短时间内重复跑多次测试,在这种情况下,最好完成测试后主动退出。