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失效),在服务器安全系统拒绝重复登录的情况下,同一用户不能再次登录。有时候我们可能需要短时间内重复跑多次测试,在这种情况下,最好完成测试后主动退出。

posted @ 2017-07-11 20:23  李崎荷  阅读(366)  评论(0编辑  收藏  举报