Servlet单元测试

时间:2020-01-09 10:35:39  来源:igfitidea点击:

单元测试servlet可能有些棘手。之所以棘手,是因为servlet依赖于servlet容器和几个servlet容器接口。为了正确地测试servlet,我们或者必须在真实的servlet容器中运行它,或者创建一个可以通过代码在单元测试期间激活的模拟servlet容器。无论哪种方式,正确设置都需要一些工作。

我通常要做的是"将代码移出边界类",正如我在"可测试性设计"一文中所述。基本上,如果可能的话,我将尝试将Servlet中的主要业务逻辑推入一个独立于Servlet API的独立类中。

首先,让我们看一个非常简单的servlet:

public class MyServlet extends HttpServlet {

    protected void doGet(HttpServletRequest request,
                         HttpServletResponse response)
            throws ServletException, IOException {

        String string1 = request.getParameter("string1");
        String string2 = request.getParameter("string2");
        String string3 = request.getParameter("string3");

        String concatenated =
                string1 + "," +
                string2 + "," +
                string3;

        response.getWriter().write(concatenated);
        response.getWriter().flush();
    }
}

该Servlet仅从请求中获取3个参数,将它们连接成一个字符串(逗号分隔),然后将响应写回到浏览器。它不是一个非常有用的servlet,但是我一直非常简单地使它易于理解我在说什么。

真正的业务逻辑实际上仅由串联逻辑组成。剩下的就是我所谓的"调度"逻辑或者"边界"逻辑。因此,我将采用串联逻辑并进入一个单独的类,该类可以独立于MyServlet类进行测试。

这是重新设计的servlet。该代码实际上看起来并不比以前的servlet小很多,但是请记住,这是一个非常简单的示例。更改后的代码以粗体标记。

public class MyServlet extends HttpServlet {

    protected void doGet(HttpServletRequest request,
                         HttpServletResponse response)
            throws ServletException, IOException {

        String string1 = request.getParameter("string1");
        String string2 = request.getParameter("string2");
        String string3 = request.getParameter("string3");

        String concatenated =
            MyConcatenator.concatenate(
            string1, string2, string3);
        
        response.getWriter().write(concatenated);
        response.getWriter().flush();
    }
}

注意串联逻辑如何被移动到名为MyConcatenator的类。 servlet剩下的唯一事情就是边界逻辑,即从请求对象中取出请求参数,并将连接后的字符串写回到响应对象中。这种边界逻辑非常简单,并且项目可能不需要自动进行单元测试就可以生存。

这是MyConcatenator类的样子。是的,这很简单,但是再次,这是因为我选择了一个非常简单的大小写连接字符串的情况。如果是图像生成,那将更加复杂。这是代码:

public class MyConcatenator {

    public static String concatenate(String ... strings){
        StringBuilder builder = new StringBuilder();

        for(int i = 0; i<strings.length; i++){
            if(i>0){
                builder.append(",");
            }
            builder.append(strings[i]);
        }

        return builder.toString();
    }
}

注意,concatenate()方法是如何仅引用字符串数组的。没有引用servlet特定的接口。这使得为MyConcatenator分别编写单元测试变得非常容易。这是这样的单元测试:

import org.junit.Test;
import static junit.framework.Assert.*;

public class MyConcatenatorTest {

    @Test
    public void testConcatenate(){
        String concatenated =
                MyConcatenator.concatenate(
                        "one", "two", "three", "four");

        assertEquals("one,two,three,four", concatenated);
    }
}

再次注意,在此单元测试中,没有必要引用任何servlet类或者接口。