Como testar meu servlet usando JUnit


112

Eu criei um sistema web usando Java Servlets e agora quero fazer o teste JUnit. My dataManageré apenas um código básico que o envia ao banco de dados. Como você testaria um Servlet com JUnit?

Meu exemplo de código que permite a um usuário se registrar / inscrever, que é enviado da minha página principal 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);
    }  
}

Respostas:


169

Você pode fazer isso usando o Mockito para que o mock retorne os parâmetros corretos, verifique se eles foram realmente chamados (opcionalmente, especifique o número de vezes), escreva o 'resultado' e verifique se está correto.

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"));
    }
}

Dessa forma, como você garante que o "Cache-Control" seja definido na resposta?
Markus Schulte de

34
Em vez de imprimir em um arquivo real no disco, você pode usar um StringWriter (como um parâmetro para o construtor de PrintWriter). Em seguida, você assertTrue (stringWriter.toString (). Contains ("My Expected String")); Dessa forma, o teste irá ler / gravar memória em vez de disco.
spg

@aaronvargas: Obrigado pela resposta! Mas quando executo seu código, recebo o seguinte erro: java.util.MissingResourceException: Não é possível encontrar o pacote para o nome de base javax.servlet.LocalStrings, locale de_DE - Acontece durante a execução de novo MyServlet (). DoPost ( ...). Alguma ideia do que pode ser quebrado?
Benny Neugebauer

1
@BennyNeugebauer, parece que o pacote não está no caminho de classe. Eu escreveria outro teste JUnit que apenas obtém um valor do Bundle para isolar o problema.
aaronvargas

@aaronvargas, obrigado pelo seu feedback! Eu encontrei uma solução para isso. Eu tive que "javax.servlet-api" para minhas dependências no meu pom.xml.
Benny Neugebauer

49

Em primeiro lugar, em um aplicativo real, você nunca obteria informações de conexão de banco de dados em um servlet; você o configuraria em seu servidor de aplicativos.

Existem maneiras, no entanto, de testar Servlets sem ter um contêiner em execução. Uma é usar objetos fictícios. O Spring fornece um conjunto de simulações muito úteis para coisas como HttpServletRequest, HttpServletResponse, HttpServletSession, etc:

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

Usando esses mocks, você pode testar coisas como

O que acontece se o nome de usuário não estiver na solicitação?

O que acontece se o nome de usuário estiver na solicitação?

etc

Você poderia então fazer coisas como:

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
    }
}

3

Acho que os testes Selenium são mais úteis com integração ou teste funcional (ponta a ponta). Estou tentando usar org.springframework.mock.web , mas não estou muito longe. Estou anexando um controlador de amostra a um conjunto de testes jMock .

Primeiro, o Controlador:

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;
            }
        }
    }

}

A seguir, o teste:

    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));
        }

    }

Espero que isso possa ajudar.


3

Atualizado em fevereiro de 2018: OpenBrace Limited foi fechado e seu produto ObMimic não é mais compatível.

Aqui está outra alternativa, usando a biblioteca ObMimic da API Servlet do OpenBrace (revelação: sou o desenvolvedor).

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());

    }

}

Notas:

  • Cada "mimic" tem um objeto "mimicState" para seu estado lógico. Isso fornece uma distinção clara entre os métodos da API do Servlet e a configuração e inspeção do estado interno do mímico.

  • Você pode se surpreender ao saber que a verificação de Content-Type inclui "charset = ISO-8859-1". No entanto, para o código "doPost" fornecido, é de acordo com o Javadoc da API Servlet e o método getContentType do próprio HttpServletResponse e o cabeçalho Content-Type real produzido, por exemplo, no Glassfish 3. Você pode não perceber isso se estiver usando objetos fictícios normais e seu próprias expectativas do comportamento da API. Nesse caso, provavelmente não importa, mas em casos mais complexos, esse é o tipo de comportamento imprevisto da API que pode zombar um pouco!

  • Eu usei response.getMimicState().getContentType()como a maneira mais simples de verificar o Content-Type e ilustrar o ponto acima, mas você poderia verificar se há "text / html" por conta própria se quiser (usando response.getMimicState().getContentTypeMimeType()). Verificar o cabeçalho Content-Type da mesma maneira que o cabeçalho Cache-Control também funciona.

  • Neste exemplo, o conteúdo da resposta é verificado como dados de caractere (com isso usando a codificação do Escritor). Também poderíamos verificar se o Writer da resposta foi usado em vez de seu OutputStream (usando response.getMimicState().isWritingCharacterContent()), mas percebi que estamos apenas preocupados com a saída resultante e não nos importamos com quais chamadas de API a produziram (embora isso possa ser verificado também ...). Também é possível recuperar o conteúdo do corpo da resposta como bytes, examinar o estado detalhado do Writer / OutputStream etc.

Há detalhes completos do ObMimic e um download gratuito no site do OpenBrace . Ou você pode entrar em contato comigo se tiver alguma dúvida (os dados para contato estão no site).


2

EDITAR : Cactus agora é um projeto morto: http://attic.apache.org/projects/jakarta-cactus.html


Você pode querer olhar para o cacto.

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

Descrição do Projeto

Cactus é uma estrutura de teste simples para testar a unidade de código java do lado do servidor (Servlets, EJBs, Tag Libs, Filtros, ...).

A intenção do Cactus é reduzir o custo de escrever testes para código do lado do servidor. Ele usa JUnit e o estende.

O Cactus implementa uma estratégia in-container, o que significa que os testes são executados dentro do container.


2

Outra abordagem seria criar um servidor embutido para "hospedar" seu servlet, permitindo que você escreva chamadas nele com bibliotecas destinadas a fazer chamadas para servidores reais (a utilidade desta abordagem depende de quão facilmente você pode tornar programático "legítimo" chamadas para o servidor - eu estava testando um ponto de acesso JMS (Java Messaging Service), para o qual clientes abundam).

Existem duas rotas diferentes que você pode seguir - as duas usuais são tomcat e jetty.

Aviso: algo para se ter em mente ao escolher o servidor a incorporar é a versão da servlet-api que você está usando (a biblioteca que fornece classes como HttpServletRequest). Se você estiver usando 2.5, descobri que o Jetty 6.x funciona bem (que é o exemplo que darei a seguir). Se você estiver usando servlet-api 3.0, o material embarcado tomcat-7 parece ser uma boa opção, no entanto, tive que abandonar minha tentativa de usá-lo, pois o aplicativo que estava testando usava servlet-api 2.5. Tentar misturar os dois resultará em NoSuchMethod e outras exceções ao tentar configurar ou iniciar o servidor.

Você pode configurar um servidor como este (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();
}

Além disso, se você escolher investigar injeção de dependência, provavelmente irá executar o Spring. Spring usa contextos para pesquisar itens injetados. Se seu servlet acabar usando spring, você pode fornecer a ele o mesmo contexto do teste adicionando o seguinte ao método acima (antes da chamada inicial): XmlWebApplicationContext wctx = new XmlWebApplicationContext (); wctx.setParent (yourAppContext); wctx.setConfigLocation (""); wctx.setServletContext (servletContext); wctx.refresh (); servletContext.setAttribute (WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, wctx);
Romeara

1

Use Selenium para testes de unidade baseados na web. Há um plugin do Firefox chamado Selenium IDE que pode gravar ações na página da web e exportar para casos de teste JUnit que usa Selenium RC para executar o servidor de teste.


Obrigado por isso parece bom, mas realmente não testa os métodos / código de servlet, não é diretamente? ou eu estou errado.
Lunar

Ele faz, disparando solicitações HTTP programaticamente.
BalusC

1
 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);

}

}

0

Primeiro, você provavelmente deve refatorar isso um pouco para que o DataManager não seja criado no código doPost. Você deve tentar a injeção de dependência para obter uma instância. (Veja o Guice vídeo para uma boa introdução ao DI.). Se você está sendo instruído a começar o teste de unidade de tudo, o DI é essencial.

Depois que suas dependências forem injetadas, você pode testar sua classe isoladamente.

Para realmente testar o servlet, existem outros threads mais antigos que discutiram isso .. tente aqui e aqui .


Ok, obrigado por seus comentários, você está dizendo que o DataManager deve ser criado dentro de um método dentro desse servlet? Assisti ao vídeo e não entendi direito :( muito novo em java, e nunca fiz nenhum tipo de teste.
Lunar

Dê uma olhada naquele vídeo do Guice (pelo menos o começo) - ele explica bem por que você nunca deseja instanciar um novo objeto em uma classe que você planeja testar em unidade.
Roy Truelove,
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.