Comment tester mon servlet en utilisant JUnit

j'ai créé un système web en utilisant des Servlets Java et je veux maintenant faire des tests JUnit. Mon dataManager est un morceau de code qui la soumet à la base de données. Comment tester un Servlet avec JUnit?

mon exemple de code qui permet à un utilisateur de s'enregistrer/s'inscrire, qui est soumis à partir de ma page principale via AJAX:

public void doPost(HttpServletRequest request, HttpServletResponse response) 
         throws ServletException, IOException{

    // Get parameters
    String userName = request.getParameter("username");
    String password = request.getParameter("password");
    String name = request.getParameter("name");

    try {

        // Load the database driver
        Class.forName("com.mysql.jdbc.Driver");

        //pass reg details to datamanager       
        dataManager = new DataManager();
        //store result as string
        String result = dataManager.register(userName, password, name);

        //set response to html + no cache
        response.setContentType("text/html");
        response.setHeader("Cache-Control", "no-cache");
        //send response with register result
        response.getWriter().write(result);

    } catch(Exception e){
        System.out.println("Exception is :" + e);
    }  
}
99
demandé sur UserNotFoundException 2011-03-25 18:17:05

10 réponses

vous pouvez le faire en utilisant Mockito pour que la simulation retourne les bonnes params, vérifier qu'elles ont bien été appelées (préciser le nombre de fois), écrire le "résultat" et vérifier que c'est correct.

import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import java.io.*;
import javax.servlet.http.*;
import org.apache.commons.io.FileUtils;
import org.junit.Test;

public class TestMyServlet extends Mockito{

    @Test
    public void testServlet() throws Exception {
        HttpServletRequest request = mock(HttpServletRequest.class);       
        HttpServletResponse response = mock(HttpServletResponse.class);    

        when(request.getParameter("username")).thenReturn("me");
        when(request.getParameter("password")).thenReturn("secret");

        StringWriter stringWriter = new StringWriter();
        PrintWriter writer = new PrintWriter(stringWriter);
        when(response.getWriter()).thenReturn(writer);

        new MyServlet().doPost(request, response);

        verify(request, atLeast(1)).getParameter("username"); // only if you want to verify username was called...
        writer.flush(); // it may not have been flushed yet...
        assertTrue(stringWriter.toString().contains("My expected string"));
    }
}
151
répondu aaronvargas 2018-07-13 13:10:43

tout d'abord, dans une application réelle, vous n'obtiendriez jamais les informations de connexion à une base de données dans un servlet; vous les configureriez dans votre serveur app.

il y a cependant des moyens de tester les Servlets sans que le conteneur ne fonctionne. L'une consiste à utiliser des objets simulés. Le printemps fournit un ensemble de mocks très utiles pour des choses comme HttpServletRequest, HttpServletResponse, HttpServletSession, etc:

http://static.springsource.org/spring/docs/3.0.x/api/org/springframework/mock/web/package-summary.html

en utilisant ces moqueries, vous pourriez tester des choses comme

que se passe-t-il si le nom d'utilisateur n'est pas dans la demande?

que se passe-t-il si le nom d'utilisateur est dans la demande?

etc

vous pourriez alors faire des choses comme:

import static org.junit.Assert.assertEquals;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.junit.Before;
import org.junit.Test;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;

public class MyServletTest {
    private MyServlet servlet;
    private MockHttpServletRequest request;
    private MockHttpServletResponse response;

    @Before
    public void setUp() {
        servlet = new MyServlet();
        request = new MockHttpServletRequest();
        response = new MockHttpServletResponse();
    }

    @Test
    public void correctUsernameInRequest() throws ServletException, IOException {
        request.addParameter("username", "scott");
        request.addParameter("password", "tiger");

        servlet.doPost(request, response);

        assertEquals("text/html", response.getContentType());

        // ... etc
    }
}
42
répondu Paul Croarkin 2011-03-28 21:54:00

Updated Feb 2018: OpenBrace Limited a fermé , et son produit ObMimic n'est plus supporté.

Voici une autre alternative, en utilisant ObMimic library of Servlet API test-doubles (disclosure: i'm its developer) D'OpenBrace.

package com.openbrace.experiments.examplecode.stackoverflow5434419;

import static org.junit.Assert.*;
import com.openbrace.experiments.examplecode.stackoverflow5434419.YourServlet;
import com.openbrace.obmimic.mimic.servlet.ServletConfigMimic;
import com.openbrace.obmimic.mimic.servlet.http.HttpServletRequestMimic;
import com.openbrace.obmimic.mimic.servlet.http.HttpServletResponseMimic;
import com.openbrace.obmimic.substate.servlet.RequestParameters;
import org.junit.Before;
import org.junit.Test;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * Example tests for {@link YourServlet#doPost(HttpServletRequest,
 * HttpServletResponse)}.
 *
 * @author Mike Kaufman, OpenBrace Limited
 */
public class YourServletTest {

    /** The servlet to be tested by this instance's test. */
    private YourServlet servlet;

    /** The "mimic" request to be used in this instance's test. */
    private HttpServletRequestMimic request;

    /** The "mimic" response to be used in this instance's test. */
    private HttpServletResponseMimic response;

    /**
     * Create an initialized servlet and a request and response for this
     * instance's test.
     *
     * @throws ServletException if the servlet's init method throws such an
     *     exception.
     */
    @Before
    public void setUp() throws ServletException {
        /*
         * Note that for the simple servlet and tests involved:
         * - We don't need anything particular in the servlet's ServletConfig.
         * - The ServletContext isn't relevant, so ObMimic can be left to use
         *   its default ServletContext for everything.
         */
        servlet = new YourServlet();
        servlet.init(new ServletConfigMimic());
        request = new HttpServletRequestMimic();
        response = new HttpServletResponseMimic();
    }

    /**
     * Test the doPost method with example argument values.
     *
     * @throws ServletException if the servlet throws such an exception.
     * @throws IOException if the servlet throws such an exception.
     */
    @Test
    public void testYourServletDoPostWithExampleArguments()
            throws ServletException, IOException {

        // Configure the request. In this case, all we need are the three
        // request parameters.
        RequestParameters parameters
            = request.getMimicState().getRequestParameters();
        parameters.set("username", "mike");
        parameters.set("password", "xyz#zyx");
        parameters.set("name", "Mike");

        // Run the "doPost".
        servlet.doPost(request, response);

        // Check the response's Content-Type, Cache-Control header and
        // body content.
        assertEquals("text/html; charset=ISO-8859-1",
            response.getMimicState().getContentType());
        assertArrayEquals(new String[] { "no-cache" },
            response.getMimicState().getHeaders().getValues("Cache-Control"));
        assertEquals("...expected result from dataManager.register...",
            response.getMimicState().getBodyContentAsString());

    }

}

Notes:

  • chaque "mimic" a un objet "mimicState" pour son état logique. Cela fournit un distinction claire entre les méthodes de L'API de Servlet et la configuration et l'inspection de l'état interne du mimic.

  • vous pourriez être surpris que la vérification du type de contenu inclue "charset=ISO-8859-1". Cependant, pour le code "doPost" donné, ceci est conforme à L'API Servlet Javadoc et à la méthode Getcontenttype de HttpServletResponse, ainsi qu'à l'en-tête contenu-Type réel produit sur Glassfish 3 par exemple. Vous pourriez ne pas réaliser ce si normal les objets fantaisie et de vos propres attentes de l'API de comportement. Dans ce cas, cela n'a probablement pas d'importance, mais dans les cas plus complexes, c'est le genre de comportement inattendu de L'API qui peut faire un peu de moquerie!

  • j'ai utilisé response.getMimicState().getContentType() comme la façon la plus simple de vérifier le contenu-Type et d'illustrer le point ci-dessus, mais vous pouvez en effet vérifier pour "texte/html" sur son propre si vous vouliez (en utilisant response.getMimicState().getContentTypeMimeType() ). La vérification de l'entête Content-Type la même façon que pour L'en-tête Cache-Control fonctionne aussi.

  • dans cet exemple, le contenu de la réponse est vérifié en tant que données de caractère (en utilisant le codage de L'auteur). Nous pourrions également vérifier que le rédacteur de la réponse a été utilisé plutôt que son OutputStream (en utilisant response.getMimicState().isWritingCharacterContent() ), mais j'ai pris que nous sommes seulement concernés par la sortie résultante, et ne se soucient pas quels appels API l'a produit (bien que cela pourrait être vérifié aussi...). Il est également possible pour récupérer le contenu du corps de la réponse en octets, examiner l'état détaillé de L'auteur/OutputStream etc.

vous trouverez tous les détails de ObMimic et un téléchargement gratuit sur le site OpenBrace . Ou vous pouvez me contacter si vous avez des questions (les coordonnées sont sur le site).

3
répondu Mike Kaufman 2018-02-27 14:16:57

je trouve que les tests au sélénium sont plus utiles avec les tests d'intégration ou fonctionnels (de bout en bout). Je travaille avec essayer d'utiliser org.springframework.moquer.web , mais je ne suis pas très loin. Je joins un contrôleur d'échantillon avec une suite de test jMock .

tout D'abord, le contrôleur:

package com.company.admin.web;

import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.bind.support.SessionStatus;

import com.company.admin.domain.PaymentDetail;
import com.company.admin.service.PaymentSearchService;
import com.company.admin.service.UserRequestAuditTrail;
import com.company.admin.web.form.SearchCriteria;

/**
 * Controls the interactions regarding to the refunds.
 * 
 * @author slgelma
 *
 */
@Controller
@SessionAttributes({"user", "authorization"})
public class SearchTransactionController {

    public static final String SEARCH_TRANSACTION_PAGE = "searchtransaction";

    private PaymentSearchService searchService;
    //private Validator searchCriteriaValidator;
    private UserRequestAuditTrail notifications;

    @Autowired
    public void setSearchService(PaymentSearchService searchService) {
        this.searchService = searchService;
    }

    @Autowired
    public void setNotifications(UserRequestAuditTrail notifications) {
        this.notifications = notifications;
    }

    @RequestMapping(value="/" + SEARCH_TRANSACTION_PAGE)
    public String setUpTransactionSearch(Model model) {
        SearchCriteria searchCriteria = new SearchCriteria();
        model.addAttribute("searchCriteria", searchCriteria);
        notifications.transferTo(SEARCH_TRANSACTION_PAGE);
        return SEARCH_TRANSACTION_PAGE;
    }

    @RequestMapping(value="/" + SEARCH_TRANSACTION_PAGE, method=RequestMethod.POST, params="cancel")
    public String cancelSearch() {
        notifications.redirectTo(HomeController.HOME_PAGE);
        return "redirect:/" + HomeController.HOME_PAGE;
    }

    @RequestMapping(value="/" + SEARCH_TRANSACTION_PAGE, method=RequestMethod.POST, params="execute")
    public String executeSearch(
            @ModelAttribute("searchCriteria") @Valid SearchCriteria searchCriteria,
            BindingResult result, Model model,
            SessionStatus status) {
        //searchCriteriaValidator.validate(criteria, result);
        if (result.hasErrors()) {
            notifications.transferTo(SEARCH_TRANSACTION_PAGE);
            return SEARCH_TRANSACTION_PAGE;
        } else {
            PaymentDetail payment = 
                searchService.getAuthorizationFor(searchCriteria.geteWiseTransactionId());
            if (payment == null) {
                ObjectError error = new ObjectError(
                        "eWiseTransactionId", "Transaction not found");
                result.addError(error);
                model.addAttribute("searchCriteria", searchCriteria);
                notifications.transferTo(SEARCH_TRANSACTION_PAGE);
                return SEARCH_TRANSACTION_PAGE;
            } else {
                model.addAttribute("authorization", payment);
                notifications.redirectTo(PaymentDetailController.PAYMENT_DETAIL_PAGE);
                return "redirect:/" + PaymentDetailController.PAYMENT_DETAIL_PAGE;
            }
        }
    }

}

ensuite, le test:

    package test.unit.com.company.admin.web;

    import static org.hamcrest.Matchers.containsString;
    import static org.hamcrest.Matchers.equalTo;
    import static org.junit.Assert.assertThat;

    import org.jmock.Expectations;
    import org.jmock.Mockery;
    import org.jmock.integration.junit4.JMock;
    import org.jmock.integration.junit4.JUnit4Mockery;
    import org.junit.Before;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.ui.Model;
    import org.springframework.validation.BindingResult;
    import org.springframework.validation.ObjectError;
    import org.springframework.web.bind.support.SessionStatus;

    import com.company.admin.domain.PaymentDetail;
    import com.company.admin.service.PaymentSearchService;
    import com.company.admin.service.UserRequestAuditTrail;
    import com.company.admin.web.HomeController;
    import com.company.admin.web.PaymentDetailController;
    import com.company.admin.web.SearchTransactionController;
    import com.company.admin.web.form.SearchCriteria;

    /**
     * Tests the behavior of the SearchTransactionController.
     * @author slgelma
     *
     */
    @RunWith(JMock.class)
    public class SearchTransactionControllerTest {

        private final Mockery context = new JUnit4Mockery(); 
        private final SearchTransactionController controller = new SearchTransactionController();
        private final PaymentSearchService searchService = context.mock(PaymentSearchService.class);
        private final UserRequestAuditTrail notifications = context.mock(UserRequestAuditTrail.class);
        private final Model model = context.mock(Model.class);


        /**
         * @throws java.lang.Exception
         */
        @Before
        public void setUp() throws Exception {
            controller.setSearchService(searchService);
            controller.setNotifications(notifications);
        }

        @Test
        public void setUpTheSearchForm() {

            final String target = SearchTransactionController.SEARCH_TRANSACTION_PAGE;

            context.checking(new Expectations() {{
                oneOf(model).addAttribute(
                        with(any(String.class)), with(any(Object.class)));
                oneOf(notifications).transferTo(with(any(String.class)));
            }});

            String nextPage = controller.setUpTransactionSearch(model);
            assertThat("Controller is not requesting the correct form", 
                    target, equalTo(nextPage));
        }

        @Test
        public void cancelSearchTest() {

            final String target = HomeController.HOME_PAGE;

            context.checking(new Expectations(){{
                never(model).addAttribute(with(any(String.class)), with(any(Object.class)));
                oneOf(notifications).redirectTo(with(any(String.class)));
            }});

            String nextPage = controller.cancelSearch();
            assertThat("Controller is not requesting the correct form", 
                    nextPage, containsString(target));
        }

        @Test
        public void executeSearchWithNullTransaction() {

            final String target = SearchTransactionController.SEARCH_TRANSACTION_PAGE;

            final SearchCriteria searchCriteria = new SearchCriteria();
            searchCriteria.seteWiseTransactionId(null);

            final BindingResult result = context.mock(BindingResult.class);
            final SessionStatus status = context.mock(SessionStatus.class);

            context.checking(new Expectations() {{
                allowing(result).hasErrors(); will(returnValue(true));
                never(model).addAttribute(with(any(String.class)), with(any(Object.class)));
                never(searchService).getAuthorizationFor(searchCriteria.geteWiseTransactionId());
                oneOf(notifications).transferTo(with(any(String.class)));
            }});

            String nextPage = controller.executeSearch(searchCriteria, result, model, status);
            assertThat("Controller is not requesting the correct form", 
                    target, equalTo(nextPage));
        }

        @Test
        public void executeSearchWithEmptyTransaction() {

            final String target = SearchTransactionController.SEARCH_TRANSACTION_PAGE;

            final SearchCriteria searchCriteria = new SearchCriteria();
            searchCriteria.seteWiseTransactionId("");

            final BindingResult result = context.mock(BindingResult.class);
            final SessionStatus status = context.mock(SessionStatus.class);

            context.checking(new Expectations() {{
                allowing(result).hasErrors(); will(returnValue(true));
                never(model).addAttribute(with(any(String.class)), with(any(Object.class)));
                never(searchService).getAuthorizationFor(searchCriteria.geteWiseTransactionId());
                oneOf(notifications).transferTo(with(any(String.class)));
            }});

            String nextPage = controller.executeSearch(searchCriteria, result, model, status);
            assertThat("Controller is not requesting the correct form", 
                    target, equalTo(nextPage));
        }

        @Test
        public void executeSearchWithTransactionNotFound() {

            final String target = SearchTransactionController.SEARCH_TRANSACTION_PAGE;
            final String badTransactionId = "badboy"; 
            final PaymentDetail transactionNotFound = null;

            final SearchCriteria searchCriteria = new SearchCriteria();
            searchCriteria.seteWiseTransactionId(badTransactionId);

            final BindingResult result = context.mock(BindingResult.class);
            final SessionStatus status = context.mock(SessionStatus.class);

            context.checking(new Expectations() {{
                allowing(result).hasErrors(); will(returnValue(false));
                atLeast(1).of(model).addAttribute(with(any(String.class)), with(any(Object.class)));
                oneOf(searchService).getAuthorizationFor(with(any(String.class)));
                    will(returnValue(transactionNotFound));
                oneOf(result).addError(with(any(ObjectError.class)));
                oneOf(notifications).transferTo(with(any(String.class)));
            }});

            String nextPage = controller.executeSearch(searchCriteria, result, model, status);
            assertThat("Controller is not requesting the correct form", 
                    target, equalTo(nextPage));
        }

        @Test
        public void executeSearchWithTransactionFound() {

            final String target = PaymentDetailController.PAYMENT_DETAIL_PAGE;
            final String goodTransactionId = "100000010";
            final PaymentDetail transactionFound = context.mock(PaymentDetail.class);

            final SearchCriteria searchCriteria = new SearchCriteria();
            searchCriteria.seteWiseTransactionId(goodTransactionId);

            final BindingResult result = context.mock(BindingResult.class);
            final SessionStatus status = context.mock(SessionStatus.class);

            context.checking(new Expectations() {{
                allowing(result).hasErrors(); will(returnValue(false));
                atLeast(1).of(model).addAttribute(with(any(String.class)), with(any(Object.class)));
                oneOf(searchService).getAuthorizationFor(with(any(String.class)));
                    will(returnValue(transactionFound));
                oneOf(notifications).redirectTo(with(any(String.class)));
            }});

            String nextPage = controller.executeSearch(searchCriteria, result, model, status);
            assertThat("Controller is not requesting the correct form", 
                    nextPage, containsString(target));
        }

    }

j'espère que cela pourrait aider.

2
répondu Steve Gelman 2011-10-04 16:26:21
 public class WishServletTest {
 WishServlet wishServlet;
 HttpServletRequest mockhttpServletRequest;
 HttpServletResponse mockhttpServletResponse;

@Before
public void setUp(){
    wishServlet=new WishServlet();
    mockhttpServletRequest=createNiceMock(HttpServletRequest.class);
    mockhttpServletResponse=createNiceMock(HttpServletResponse.class);
}

@Test
public void testService()throws Exception{
    File file= new File("Sample.txt");
    File.createTempFile("ashok","txt");
    expect(mockhttpServletRequest.getParameter("username")).andReturn("ashok");
    expect(mockhttpServletResponse.getWriter()).andReturn(new PrintWriter(file));
    replay(mockhttpServletRequest);
    replay(mockhttpServletResponse);
    wishServlet.doGet(mockhttpServletRequest, mockhttpServletResponse);
    FileReader fileReader=new FileReader(file);
    int count = 0;
    String str = "";
    while ( (count=fileReader.read())!=-1){
        str=str+(char)count;
    }

    Assert.assertTrue(str.trim().equals("Helloashok"));
    verify(mockhttpServletRequest);
    verify(mockhttpServletResponse);

}

}
2
répondu ashok 2012-08-07 11:50:42

MODIFIER : Cactus est maintenant un projet mort: http://attic.apache.org/projects/jakarta-cactus.html


vous pourriez vouloir regarder cactus.

http://jakarta.apache.org/cactus /

Description Du Projet

Cactus est un cadre d'essai simple pour l'unité test côté serveur de code java (Servlets, Ejb, Tag Libs, des Filtres, des ...).

L'intention de Cactus est de réduire le coût d'écriture des tests pour le code côté serveur. Il utilise JUnit et l'étend.

Cactus met en œuvre une stratégie en conteneur, ce qui signifie que les tests sont effectués à l'intérieur du conteneur.

2
répondu Crispy 2014-04-01 23:57:45

utiliser sélénium pour les essais à l'unité sur bobines. Il y a un plugin Firefox appelé Selenium IDE qui peut enregistrer des actions sur la page Web et exporter vers JUnit testcases qui utilise Selenium RC pour exécuter le serveur de test.

1
répondu BalusC 2011-03-25 15:37:31

une autre approche serait de créer un serveur intégré pour" héberger "votre servlet, vous permettant d'écrire des appels contre lui avec des bibliothèques destinées à faire des appels vers des serveurs réels (l'utilité de cette approche dépend un peu de la facilité avec laquelle vous pouvez faire des appels programmatiques" légitimes " vers le serveur - j'étais en train de tester un point d'accès JMS (Java Messaging Service), pour lequel les clients abondent).

Il ya un couple de routes différentes, vous pouvez aller - les deux habituelles sont tomcat et de la jetée.

Avertissement: quelque chose d'être conscient au moment de choisir le serveur à intégrer est la version de servlet-api que vous utilisez (la bibliothèque qui fournit des classes comme HttpServletRequest). Si vous utilisez 2.5, j'ai trouvé Jetty 6.x fonctionne bien (ce qui est l'exemple que je vais donner ci-dessous). Si vous utilisez servlet-api 3.0, le tomcat-7 embedded stuff semble être une bonne option, cependant j'ai dû abandonner ma tentative de l'utiliser, car l'application que je testais utilisait servlet-api 2.5. Essayer de mélanger les deux aboutira à NoSuchMethod et d'autres exceptions similaires lors de la tentative de configurer ou de démarrer le serveur.

vous pouvez configurer un tel serveur comme ceci( Jetty 6.1.26, servlet-api 2.5):

public void startServer(int port, Servlet yourServletInstance){
    Server server = new Server(port);
    Context root = new Context(server, "/", Context.SESSIONS);

    root.addServlet(new ServletHolder(yourServletInstance), "/servlet/context/path");

    //If you need the servlet context for anything, such as spring wiring, you coudl get it like this
    //ServletContext servletContext = root.getServletContext();

    server.start();
}
1
répondu romeara 2014-10-28 18:44:01

tout d'abord, vous devriez probablement remanier ce un peu pour que le DataManager ne soit pas créé dans le code doPost.. vous devriez essayer L'Injection de dépendances pour obtenir une instance. (Voir la vidéo Guice pour une belle introduction à DI.). Si on vous dit de tout tester à l'unité, alors DI est un must-have.

une fois vos dépendances injectées, vous pouvez tester votre classe isolément.

pour réellement tester le servlet, il y a d'autres vieux fils qui ont discuté de cela.. essayez ici et ici .

0
répondu Roy Truelove 2017-05-23 12:02:38

juste incase les réponses ci-dessus ne fonctionnent plus dans les nouvelles versions de Mockito au lieu d'utiliser mock() et when() Mockito.mock() et Mockito.when() doivent être utilisés

0
répondu mosaad 2013-10-16 10:47:52