StrutsTestCase

0
11079

StrutsTestCase

Introducción

StrutsTestCase es una extensi&oacuten de junit que permite realizar pruebas sobre
aplicaciones basadas en Struts.

Provee dos maneras diferentes de realizar las pruebas: mediante mock objects
(las clases ServletRequest, ServletResponse y aquellas manejadas por el
servidor de aplicaciones son mock objects), o mediante Cactus (un contender de
servlets ligero). El cambiar entre cualquiera de las dos aproximaciones es tan
sencillo como cambiar la clase de la que heredará nuestro test, sin que el test
en sí cambie.

Personalmente prefiero usar mock objects en lugar de Cactus. Usar Cactus
implica que nuestro proyecto tiene más dependencias y configuraciones. Con mock
objects todo queda más sencillo, aunque dependediendo del tipo de aplicación
necesitaremos realizar ciertas inicializaciones, como veremos en el ejemplo que
sigue.

Podéis descargarlo y acceder a la documentación en http://strutstestcase.sourceforge.net/.

Otras opciones para realizar este tipo de pruebas es utilizar otras
herramientas que nos permiten simular navegaciones en cualquier aplicación web
(ya no nos estaríamos casando con struts), y como lo normal es que cada acción
funcional este asociada a un action de struts, realizaríamos pruebas sobre los
action (con salvedades como que ya dependemos de un servidor de aplicaciones y
la configuración que todo ellos conlleva). Una herramienta muy útil para estos
menesteres, Selenium, la podéis ver en el tutorial de mi compañero Víctor: http://www.adictosaltrabajo.com/tutoriales/tutoriales.php?pagina=seleniumIDE.

El código de ejemplo

Veremos cómo usar StrutsTestCase para una aplicación que usa Struts 1.3,
contruida con Maven.

La aplicación trata de una sencilla librería por internet, donde se necesita
estar logado para realizar las acciones, y finalmente se pueden realizar
búsquedas filtradas de los libro.

Veamos como ejemplo una clase de acción de la que vamos a realizar tests:

public class LoginAction extends GenericAction {

    private static final Log log = LogFactory.getLog(LoginAction.class);

    @Override
    public ActionForward executeAction(ActionMapping aM, ActionForm aF,
            HttpServletRequest request, HttpServletResponse response)
            throws Exception {

        final LoginForm form = (LoginForm) aF;
        String forw = "error";

        final String name = form.getName();
        final String pwd = form.getPwd();

        final ActionErrors errors = new ActionErrors();

        if (log.isDebugEnabled()) {
            log.debug("pasando por loginAction");
        }

        if ("test".equals(name) && "test".equals(pwd)) {
            final User logged = new User();

            logged.setLogin("test");
            logged.setPassword("test");
            logged.setNombre("German");
            logged.setApellido("Jimenez");
            logged.setEmail("gjimenez@autentia.com");

            request.getSession().setAttribute("loggedUser", logged);
            forw = "success";

        } else {

            errors.add("errors.login.fail", new ActionMessage(
                    "errors.login.fail"));
            addErrors(request, errors);
            forw = "error";
        }

        return aM.findForward(forw);
    }

}

Y la clase de la que hereda, que verifica que un usuario esté o no logado
para todas las acciones:

public abstract class GenericAction extends Action {

    private static final Log log = LogFactory.getLog(GenericAction.class);

    private boolean validateUser(ActionMapping aM, HttpServletRequest req,
            HttpServletResponse res) throws Exception {

        return req.getSession(false).getAttribute("loggedUser") != null;
    }

    /*
     * (non-Javadoc)
     * 
     * @seeorg.apache.struts.action.Action#execute(org.apache.struts.action.
     * ActionMapping, org.apache.struts.action.ActionForm,
     * javax.servlet.http.HttpServletRequest,
     * javax.servlet.http.HttpServletResponse)
     */
    @Override
    public ActionForward execute(ActionMapping aM, ActionForm aF,
            HttpServletRequest request, HttpServletResponse response)
            throws Exception {

        boolean isLogged = true;

        if (!(request.getContextPath() + "/login.do").equals(request
                .getRequestURI())) {
            isLogged = validateUser(aM, request, response);
        }

        if (log.isDebugEnabled()) {
            log.debug("Pasando por la validacion de usuario: " + isLogged);
        }

        return isLogged ? executeAction(aM, aF, request, response)
                : new ActionForward("/pages/login.jsp");
    }

    public abstract ActionForward executeAction(ActionMapping aM,
            ActionForm aF, HttpServletRequest request,
            HttpServletResponse response) throws Exception;

}

Por último nos quedaría ver la clase de test de junit con su extensión de
StrutsTestCase (veréis que es un test de JUnit 3, ya que no podemos usar test
de JUnit 4 con StrutsTestCase) (aunque JUnit 4 es capaz de ejecutar tests de
JUnit 3):

public class LibraryTest extends MockStrutsTestCase {

    @Override
    protected void setUp() throws Exception {
        super.setUp();

        final DAO dao = new BookDAO();
        final List editoriales = Arrays.asList(Editorial.EDITORIALS);
        final List author = Arrays.asList(Author.AUTHORS);

        // Debemos establecer para cada prueba el contextPath, puesto que la
        // request es un
        // mock object y nadie la establece
        getMockRequest().setContextPath("/library");

        // establecemos ciertas variables en el contexto de aplicacion que se
        // supone
        // que inicializa un listener
        getSession().getServletContext().setAttribute("editorialesSelect",
                editoriales);
        getSession().getServletContext().setAttribute("autoresSelect", author);

        try {
            // Anadimos los datos de prueba a nuestro DAO
            // lo ideal seria utilizar mock objects e integrar con Spring
            dao.update(new Book("El fuego", "", Integer.valueOf(1), "", "",
                    Float.valueOf(0), editoriales.get(0), author.get(0)));
            dao.update(new Book("Luna nueva", "", Integer.valueOf(1), "", "",
                    Float.valueOf(0), editoriales.get(1), author.get(1)));
            dao.update(new Book("Crepusculo", "", Integer.valueOf(1), "", "",
                    Float.valueOf(0), editoriales.get(2), author.get(1)));
            dao.update(new Book("El club de las malas madres", "", Integer
                    .valueOf(1), "", "", Float.valueOf(0), editoriales.get(5),
                    author.get(3)));
            dao.update(new Book(
                    "El señor de los anillos: la comunidad del anillo", "",
                    Integer.valueOf(1), "", "", Float.valueOf(0), editoriales
                            .get(3), author.get(4)));
            dao.update(new Book("El señor de los anillo: las dos torres", "",
                    Integer.valueOf(1), "", "", Float.valueOf(0), editoriales
                            .get(3), author.get(4)));
            dao.update(new Book("El señor de los anillo: el retorno del Rey",
                    "", Integer.valueOf(1), "", "", Float.valueOf(0),
                    editoriales.get(3), author.get(4)));
        } catch (DAOException e) {

        }

        // el dao se encuentra en el contexto de la aplicacion
        getSession().getServletContext().setAttribute("bookDao", dao);
    }

    public void testShouldLogin() {

        // Establecemos la url de la request nosotros mismos puesto
        // que la request es un mock object y nadie lo establece
        getMockRequest().setRequestURI("/library/login.do");

        // indicamos el action a probar
        setRequestPathInfo("/login");
        // andimos los parametros necesarios, que sera como
        // si enviaramos en un formulario
        addRequestParameter("name", "test");
        addRequestParameter("pwd", "test");

        // ejecutamos la accion
        actionPerform();

        // verificamos que recibimos el forward success
        verifyForward("success");

        // obtenemos el usuario logado en sesion y comprobamos su existencia
        final User logged = (User) getSession().getAttribute("loggedUser");
        assertNotNull(logged);

        // comprobamos que sea valido
        assertEquals("test", logged.getLogin());
        assertEquals("test", logged.getPassword());
        assertEquals("German", logged.getNombre());
        assertEquals("Jimenez", logged.getApellido());
        assertEquals("gjimenez@autentia.com", logged.getEmail());

        // verificamos que no existan ActionErrors
        verifyNoActionErrors();

    }

    public void testShouldLoginFail() {

        // Establecemos la url de la request nosotros mismos puesto
        // que la request es un mock object y nadie lo establece
        getMockRequest().setRequestURI("/library/login.do");

        // indicamos el action a probar
        setRequestPathInfo("/login");

        // anadimos los parametros necesarios, que sera como
        // si enviaramos en un formulario
        addRequestParameter("name", "testtt");
        addRequestParameter("pwd", "test");

        // ejecutamos la accion
        actionPerform();
        // verificamos que recibimos el forward success (pues usuario o
        // contrasena son invalidos)
        verifyForward("error");
        // debemos tener errores de ActionErrors
        verifyActionErrors(new String[] { "errors.login.fail" });
    }
}

Veamos ciertas características del test:

  • Si reescribimos el método SetUp, debemos explícitamente llamar a
    super.setUp()
    . Si no lo hacemos StrutsTestCase no se
    inicializará correctamente.
  • Hereda de MockStrutsTestCase (si quisieramos Cactus debe heredar de
    CactusStrutsTestCase, y configurar Cactus).
  • Al usar mock objects en la ServletRequest y ServletResponse, hay ciertos
    atributos que nadie setea (como contextPath o requestURI), pero que podemos
    o tendremos que establecer nosotros, accediendo a los objetos derequest y
    response mediante getMockRequest() y getMockResponse() y realizar las
    operaciones necesarias.
  • Sin embargo todos aquellos atributos que nuestro action ponga en sesión o
    cualquier otro contexto se mantendrá durante toda la prueba (cada uno de
    los métodos del test de JUnit), por tanto si queremos probar una búsqueda y
    necesitamos estar logados, en la prueba de búsqueda debemos logarnos
    antes.
  • Por defecto busca el fichero struts-config.xml. Si tuviéramos diferentes
    nombres se pueden indicar con el método setConfigFile(pathname).
  • Los ficheros web.xml y struts-config.xml deben estar en
    el classpath para que StrutsTestCase los pueda encontrar y procesar. Una
    solución, si se realiza desde eclipse, es añadir la carpeta webapp al build
    path (StrutsTestCase busca realmente WEB-INF/web.xml y
    WEB-INF/struts-config.xml). Desde maven hemos de configurar el plugin
    surefire, como veremos a continuación

Por último veamos cómo quedaría nuestro fichero de pom, para ver las
dependencias necesarias y la configuración del plugin de surefire para añadir
la carpeta webapp al classpath:


 4.0.0
 com.autentia.training
 library
 war
 1.0-SNAPSHOT
 library-core Maven Webapp
 http://maven.apache.org

 
  
   
    org.apache.maven.plugins
    maven-surefire-plugin
    
     
      /webapp
     
    
   
  
 

 

  

   org.apache.struts
   struts-core
   1.3.10
  
  
   org.apache.struts
   struts-taglib
   1.3.10
  
  
   org.apache.struts
   struts-tiles
   1.3.10
  
  
   commons-collections
   commons-collections
   3.2.1
  
  
   strutstestcase
   strutstestcase
   2.1.4-1.2-2.4
  
 

Conclusiones

Ya veis que en Autentia nos gusta
trabajar bien, y con las últimas herramientas, que nos ahorren tiempo y
esfuerzo, asi que no dudéis en contactar con nosotros para más información.

DEJA UNA RESPUESTA

Por favor ingrese su comentario!

He leído y acepto la política de privacidad

Por favor ingrese su nombre aquí

Información básica acerca de la protección de datos

  • Responsable:
  • Finalidad:
  • Legitimación:
  • Destinatarios:
  • Derechos:
  • Más información: Puedes ampliar información acerca de la protección de datos en el siguiente enlace:política de privacidad