Teste unitário de actions

43 respostas
S

Estou pensando num esquema para testes unitários no Mentawai.

Olhando o WebWork vi que ele faz teste unitário mais ou menos assim: (se estiver esquecendo algo me avisem)

HelloWorldAction action = new HelloWorldAction();
action.setUsername("sergio");
// e a session ???
// e o application context ???
// e o locale ???

assertEquals(Action.SUCCESS, action.execute());

O mentawai poderia tb usar os setters da action para setar os parametros, se fosse o caso de uma action que utiliza o InjectionFilter e não o Input.

Entretanto penso que usando o Input, fica mais flexível:

Input input = new InputMap();
input.setValue("username", "sergio");

Context session = new ContextMap();
session.setValue("user", new User("sergio")); // contexto de sessão

Context application = new ContextMap();
application.setValue("algumacoisa", "hello"); // contexto da aplicacao

Locale loc = new Locale("pt", "BR"); // toda action possui o seu locale...

Action action = new HelloWorldAction(); // note que aqui podemos usar a interface
action.setInput(input);
action.setOutput(new OutputMap());
action.setSessionContext(session);
action.setApplicationContext(application);
action.setLocale(loc);

assertEquals(Action.SUCCESS, action.execute());

O legal desse approach é que poderíamos serializar e guardar os nossos dados de teste, isto é, o session e o application. Imagina que o objeto User que deve estar na sessão é um objeto grande que vc não quer ficar populando toda hora.

Context session = getSessionFromDisk("sergio.session.ser");
Context application = getApplicationFromDisk("myapp.application.ser");

Como podemos deixar isso bem fácil ?

43 Respostas

M

Rapaz, acho que você tá complicado a parada. O melhor mesmo seria usar objetos “falsos”, session e context são interfaces, já tem mocks pra eles, Locale é só mais um set.

O teste deve ser o mais auto-contido possível.

S

Um action do mentawai, assim como uma do WW, não sabe o que é um HttpSession nem um HttpServletRequest.

Input, Output, Action, Context são interfaces.

O InputMap e o ContextMap são os objetos falsos que vc falou. Os tais mocks.

Como passo uma session do Hibernate para dentro de um teste do WebWork ??? O que me parece legal do approach de usar Input (default do Mentawai) e não setters por injection (default do WW) é que vc pode setar tudo que vc precisa no Input, do mesmo jeito que um filtro faria.

Então fica assim:

Input input = new InputMap();
 input.setValue("username", "sergio");

 input.setValue("session", hibernate session aqui);
 
 Context session = new ContextMap();
 session.setValue("user", new User("sergio")); // contexto de sessão
 
 Context application = new ContextMap();
 application.setValue("algumacoisa", "hello"); // contexto da aplicacao
 
 Locale loc = new Locale("pt", "BR"); // toda action possui o seu locale...
 
 Action action = new HelloWorldAction(); // note que aqui podemos usar a interface

 prepareAction(action, input, session, application, loc);

 assertEquals(Action.SUCCESS, action.execute());

Como eu seto a session, o application no WebWork?

Como passo uma Connection do database para dentro de uma action do WebWork ? Tem que criar um setter pra tudo ???

M

Você vai passar uma Connection pra dentro do Action? E é o Action que faz a persistência? Feio, muito feio, principalmente depois de tanta conversa sobre ThreadLocal e filtros que teve aqui nos últimos dias.

Outra coisa, essa história de “encapsular” Session, Request, Response e Context, pra mim é pura repetição de código. Nunca vi nenhuma vantagem nisso, na verdade, só vi desvantagens, no dia que você tiver que setar um header ou pegar o referer, vou ter que ficar procurando, dando cast, pra um monte de interfaces que tem um monte de mocks prontos pra usar. Ninguém vai reutilizar um Action de um framework Web em uma aplicação Swing, porque a lógica da aplicação não deveria estar dentro do Action.

P

Bom ponto.

Sérgio,

Esqueça sessão Hibernate.
1 - Ela não devia estar ali: Action (Camada de Apresentação) conversando com Persistência só em coisas muito simples (que geralmente ficariam melhor sem MVC, sem JSP, com scripts em PHP ou Rails)

2 - Caso alguém faça essa besteira, pode utilizar um mock object (veja o JMock) e verificar se foram acessados os métodos certos.

O que você está testando é a action, não o processo todo. Todos os outros componentes devem se mocks.

S

Vcs confundem minha cabeça tooooooooooda :roll:

  1. A Connection foi apenas um exemplo !!! Porque não posso fazer uma persistencia dentro da action ??? Porque não posso criar actions que implementam a lógica do meu site, como AddUserAction, SaveUserAction, etc. ??? A connection ou a session do Hibernate sao passadas de forma transparente para dentro das actions, como se fosse um thread local. Vcs não leram isso: http://mentawai.lohis.com.br/connfilter.jsp

  2. Se a lógica de aplicação não deve estar dentro da Action, então deve estar dentro de beans que são acessados dentro da action. Então na minha cabeça dã no mesmo no final das contas!

  3. Programar com interfaces e desacoplar do container do servlet é uma vantagem sim e uma boa prática. Pode até não fazer diferença na maioria dos casos, mas é melhor fazer a coisa direito, né? Ou vc quer outro Struts? :wink:

  4. Phillip, porque vc diz que a action é a camada de apresentação ??? Pra mim o VIEW é o JSP, Velocity, Freemarket, Taglibs, etc.´

Essa observacao é interessante. Como vc faz então para testar uma action que faz login, isto é, que vai no banco checar se a senha está correta.

Vc vai criar um LoginBean e fazer um mock desse LoginBean dentro da Action ??? Copo vc vai substituir o LoginBean pelo Mock dentro da action ???

Eu acho que não é crime colocar a lógica dentro das actions!!! Mas estou aberto a discussão e aceito ser convencido do contrário. :wink:

(Já estou mexendo com o Hibernate !!!)

P

Ninguém está dizendo que você não [b/pode[/b], estou dizendo que você não deve.

Implementar regra de negócio numa action:

1 - É programação procedural. Qual a diferença de manipular estruturas de dados em uma Action Menta/Struts/WebWork e manipular structs em C?

2 - Acopla suas egras de negócio ao Framework, a HTTP e à Camada de Apresentação, péssima coisa.

Você deve definir uma camada de negócio, onde habitam seus objetos de domínio. Estes objetos são responsáveis pela modelagemd e regras de negócio do sistema, e são acessados pela camada de apresentação.

Eu concordo que é uma boa prática, mas o que o mauricio falou tem sentio. Teoricamente uma Action seria muito magra e sua lógica está ligada ao HTTP (ou protocolo usado).

Porque nesse caso específico TODO o mentawai é camada de apresnetação.

Não confunda Camada de Apresentação com Model, View, Controller, o MVC do menta, struts, web work está dentro da camada de apresentação.

Pra começar uma action não deve nunca ir ao banco, mas nesse caso você deve ter uma classe que acessa os dados, não? Como um DAO? Se não tiver, esqueça e refatore isso (aproveite para mover o código que faz essa ação pra camada de negócios), se tiver, crie um mock desse DAO e utilize no seu teste.

Como seria um LoginBean?

Não é crime, Sérgio, é simplesmente contra OOP.

Se você realmente quiser fazer isso, acho que seria melhor usar uma linguagem multi-paradigma ou estruturada, que já possui construtos para isso :wink:

S

Nós estamos com pontos de vista totalmente opostos aqui, Phillip.

Veja esse post:

http://www.guj.com.br/posts/list/4679.java

Veja lá a action: testAdicionarUsuarioOK

Ela está indo no banco de dados, seja através de uma Connection, de um session do Hibernate, de um DAO, de um bean, do que seja. De repente ele está até indo diretamente ao banco de dados, mas concordo que ter um objeto separado para fazer isso é MAIS CERTO e tb MAIS CHATO E TRABALHOSO. Acho que nesse caso é tão mais chato e trabalhoso que o mais certo não se aplica.

Então voltemos ao servlet/jsp puro !!! Pra que precisa de frameworks ??? Se a action é uma coisa magra ele pode ser um servlet que delega tudo para beans. Não acho que seja por aí…

Na minha cabeça o MVC era assim:

Model - Implementado em Beans de negócio ou direamenta nas actions.
View - JSP, Velocity, Freemarket, taglibs e HTML
Controller - O controlador do framework que liga as actions as views.

Como vc faria a autenticação do seu sistema? Pra mim é natural uma action de login (LoginAction) que pode ir diretamente ao banco ou fazer uso de um DAO ou BEAN (LoginBean) que faça isso. O grande problema de ter um DAO ou BEAN é que não vai ser fácil, pra não dizer impossível, fazer a substituição desse DAO que está hardcoded dentro da action por um mock quando vc for testar. Isso se vc não quiser testar a coisa toda, com o DAO verdadeiro mesmo. Mas isso vc disse que não queria fazer…

Usando o mentawai ou WW, como vc faria a autenticação do seu sistema ??? Não usuaria uma action pra isso ???

M

saoj:
Vcs confundem minha cabeça tooooooooooda :roll:

  1. A Connection foi apenas um exemplo !!! Porque não posso fazer uma persistencia dentro da action ??? Porque não posso criar actions que implementam a lógica do meu site, como AddUserAction, SaveUserAction, etc. ??? A connection ou a session do Hibernate sao passadas de forma transparente para dentro das actions, como se fosse um thread local. Vcs não leram isso: http://mentawai.lohis.com.br/connfilter.jsp

Veja só, ninguém disse que você não pode, disse que você não deve. Você não deve simplesmente porque você vai estar amarrando a sua lógica com o framework web. Tudo que a sua aplicação faz vai ficar dentro do Mentawai, no dia que você resolver fazer isso em uma aplicação Swing, vai levar o Mentawai com você? Meter um framework web em uma aplicação Swing? Esquisito não?

Transparente pra quem? Quem tem que ter acesso a conexão é o objeto responsável pela persistência (normalmente um DAO ou um AR), não o seu Action. A responsabilidade do Action deveria ser reunir as informações que vieram na requisição, que estão na sessão e no contexto e entregar para as classes de negócio, nada mais do que isso.

Mas como eu já disse, você pode fazer do jeito que quizer, só não reclame do resultado depois :mrgreen:

Beans que são acessados de dentro da Action são o mesmo que ter o código dentro da Action Sérgio? E a delegação de responsabilidades fica aonde?

Claro que é, se você conseguir desacoplar a aplicação da arquitetura de Servlets, o que com certeza não está acontecendo. Imagine uma implemetação de um DAO, ele encapsula o objeto que faz a conexão com o banco, a conexão com o banco, a transação, o statement e o resultado do statement (que pode ser um ResultSet ou um inteiro), ele realmente desacopla você do banco com um único objeto, eu não vi o banco de dados.

Já no Mentawai você cria um objeto pra empacotar o request, outro pro response, outro pra session e ainda outro pro contexto, você não desacoplou nada, só mudou o acoplamento, agora todo mundo fica acoplado ao Mentawai e na hora que precisar pegar um arquivo que esteja dentro da aplicação, vai ter que dar um “getServletContext()” e se acoplar a API de Sevlets do mesmo jeito, chamando um “getRealPath()”. Qual foi a vantagem de criar uma nova estrutura de classes que simplesmente imita a original? Isso só me lembra DTO, vixe!

Esse código não deveria estar na Action, deveria ter um objeto qualquer (GerenciadorDeLogin?) seria responsável por fazer isso. Lá na implementação do GerenciadorDeLogin ele poderia ter um UsuarioDao que teria um método “buscarPeloNomeSenha()”, que retornaria um usuário com o nome e senha passados.

Claro que não é, só é uma péssima prática e gera um acoplamento que deveria ser evitado.

Parabéns por ter começado com o Hibernate :smiley:

S

Não !!! Vc nunca precisa do ServletContext. Tudo do ServletContext é passado para o context, que encapsula ele. E getRealPath está na classe application manager como método estático.

http://mentawai.lohis.com.br/api/org/mentawai/core/ApplicationManager.html#getRealPath()

Quando vc cria uma action do WebWork ou do Struts quer dizer que vc não acopla sua action a esses frameworks ??? Pensa bem. Vc tem que de cara extender uma classe (ActionSupport, etc.) ou no mínimo implementar uma interface. Então como eu programo com o WW ou Struts sem ficar acoplado neles ???

A vantagem é que as interfaces Input, Ouput e Context (só são essas 3!) são 10 vezes menores e mais intuitivas que um HttpServletRequest, HttpServletResponse, HttpSession, e ServletContext. Isso é grande vantagem.

E o pior é que com o Mentawai vc pode ignorar o input e usar o esquema de setters do WW, isto é, usar o InjectionFilter para injetar todos os parametros na action. Eu sinceramente não consegui ver vantagem nisso, mas ao gosto do freguës…

Concordo que todas as regras de negócios devem estar em beans, e quando for acessar o banco tem que estar em DAOs. Só que as vezes a coisa é tão simples que construir um bean ou um DAO sõ pra isso me parece OVERKILL e me lembra EJB. Por que um LoginAction não pode ir no banco checar o username e password. Se amanhã vc mudar de framework vai ter que escrever essa action novamente. Õhhhh que problema grave! Se todo o problema da mudança fosse esse então seria muito fácil mudar. Mas tudo bem. Vcs estão certos nesse ponto: Dao ou Bean para fazer a autenticação no banco. Fica tudo desacoplado e funcionando por delegaçao.

Afinal, a action é camada de que ??? Ela esta mais para controller fazendo o meio campo entre beans, resultados e views, do que de apresentacao!

Minhas conclusões: (podem discordar!)

  1. Action é camada de controler. Pode ser MODEL mas não deve. Deve delegar a beans de negócio e/ou DAOs.

  2. Criar uma action totalmente desacoplada do framework não dá. Cada Framework tem sua action.

  3. Passar os parametros para uma Action via INPUT, acopla sua action ao INPUT. Leia o item 2) e veja que ela já estã acoplada ao seu framework de qualquer jeito. E se for muito chato em relação a isso use um injection filter e ignore o input.

P

Oi,

Não entendi o que um psot sobre o webwork ia dizer. Os usuários fazem o que querem com a ferramenta, isso não quer dizer que sigam as melhore sou piores praticas.

WebWork não vai te impedir de usar SGBD na camada de apresentação, por que ele faria isso? Isso é responsabilidade do bom-senso do programador.

MAIS TRABALHOSO, MAIS CHATO? Concordo. Também concordo que é MAIS CHATO fazer aplicações simples em java, se sua aplicação é tão simples que n/ao vai precisar de manutenção e flexibilidade, a ponto de você ignorar completamente boas práticas, use PHP ou ASP clássico e seja feliz.

Você mesmo não havia falado acima sobre fazer a coisa certa?

Porque é MVC? Por que tem mil funcionaldiades como atg libraries, chains, forwards, porque facilita o desenvolvimento? Ou você acha que um framework MVC vai concetrar tudo num sistema?

1 - ESQUEÇA ESSE PAPO DE BEANS, são objetos de negócio. javaBeans é uma especificação falida de Sun, hoje em dia significa apenas convenção de nomenclatura get/set e cosntrutor vazio (as vezes nem isso!).

Igualzinho em ASP, né?

Crie uma classe respnsável por gerenciar login/lougout e outros aspectos de segurança, como privilégios e dê essa responsabilidade para ela. Colocar lógica de negócios em Actions/Servlet ou qualquer cosia que não simples POJOs é pedir para “copiar e colar” código.

Ou use JAAS.

Sim, uma action que passa os parãmetros de Input/Request para a classe que gerencia a segurança da aplicação.

Isso é legal, mais ainda assim você pdoe precisar de um ou outro aspecto referente exclusivamnete ao HTTP que estaria disponível num objeto, mas., enfim, é um caso hipotético, em 99% das vezes a estrutura de abstração dá pro gasto.

Concordo.

Deixar lógica de negócios vezer da camada de negócios (i.e. “beans”, argh!) para outras é algo que não deve acontecer. Acontece muito com valdiação, mas isso deve ser eliminado. Você deveria centralizar a lógica da aplicação num lugar só.

Se você quer algo simples, largue JSP, Mentawai, Servlets, WebWork de lado e use PHP ou Rails! Se você quer construir uma aplicação de verdade, você rpecisa pensar no que está fazendo.

TODO o MVC está na camada de apresentação, MVC não é uma camada, não divide em camadas! É apenas um padrão!

Você está certo: actions são controladores e por isso NÃO devem ter regras de negócio, apenas despachar o fluxo pra que as tem.

Um Framework MVC controla uma INTERFACE do sistema, as regras devem estar em outro lugar, a famosa camada de negócios.

Colocar código de negócios em Actions é exatamente como colocar regras de negócios no on_click de algum botão em VB ou Delphi.

Se é chato, contra-produtivo, você tem daus escolhas: ficar com uma aplicação á la RAD ou mudar de linguagem.

S

Já me rendi a isso. Realmente tudo que é lógica de negócio tem que estar em beans (=POJOS) e/ou daos fora da action, mesmo que seja uma tentação colocar isso na action e mesmo que seja mais simples e menos chato fazer o tal, e mesmo que vc nunca porte o seu sistema para outra plataforma. Como vc e eu falamos, vamos fazer o certo e ponto. :wink:

Então o ConnectionFilter ficou sem sentido. E pior que ele já está lá a um mes e ninguem falou anda. :frowning:

Talvez ao invés do filtro meter isso na action, ele poderia meter no thread local. Acho que aí fica mais corrento, o que acham ???

Depois vou abrir outro post sobre testes unitários. Tenho algumas dúvidas ainda, tais como:

  • Criar um monte de mock objetos para DAOs, POJOS, etc náo é um saco. Não é melhor testar a porra toda com um banco de testes or exemplo.

  • Como o webwork passa a session e o application para dentro do teste da action ?

  • Não é melhor passar um INPUT e dois Contextos (sesion e application) do que ficar chamando um monte de setters da action ? Fica mais desacoplado, isto é, não fica dependendo da implementacao da action (setters) e vc pode aplicar o mesmo teste em várias actions (sei lá)…

P

Err…uhm…na verdade, eu tive um papo com o Azenha sobre isso esse dias, quando ele me mostrou o filter do hibernate :stuck_out_tongue:

Mas deixa lá, tem muita gente que faz assim. Eu pessoalmente acho que um framework produtividade 100% pra fazer pequenos aplicativos ia ser o máximo, porque eu, por exemplo, tenho um domínio onde só aceita Java (ok,a ceita outras coisas, mas vamos focar no exemplo) e se quiser colcoar em meu site um formzinho de “Fale Conosco”, vai ter que ser em java… dai é uma $%$¨fazer Servlet, Action, JSP… tinha que ser algo mais fácil!

isso é da teoria dos testes. Você não deve fazer isso, porque não vai testar o compoentne (classe ou outro) isoladamente.

Dê uma olahda no JMock, sério.

Eu gosto da idéia de “contextos genéricos”, mas acho que o maior problema dum framework web para ser mais genérico e bonitinho é o próprio esquema request/response. Em outros cenários (GUI) você não tem isso, não recebe um objeto request e consulta um objeto response.

Se bem que dá pra adaptar, por exemplo, minha Action Swing recebe um objeto request e quando faz

O objeto que implementa o request que ela recebeu vai até o JTextAlgumaCoisa chamado “nome” e retorna o valor dele… sei lá idéias…

C

javaBeans é uma especificação falida de Sun

PC, por que uma especificação fálida?

Explique melhor, kra, porque eu vejo os javaBeans como extremamente úteis e como uma boa prática de programação.

M

carneiro:
javaBeans é uma especificação falida de Sun

PC, por que uma especificação fálida?

Explique melhor, kra, porque eu vejo os javaBeans como extremamente úteis e como uma boa prática de programação.

JavaBeans como uma boa prática de programação???

Meu amigo, você deveria conhecer uma linguagem que tenha o conceito de propriedades de verdade, como C#.

Só mais uma coisinha antes que eu me esqueça, os filtros não devem, por motivo nenhum desse universo, abrir a conexão com o banco ou a sessão com o Hibernate, nunca, never, nem de brincadeira. O filtro deve ser usado pra terminar a transação e fechar uma conexão que esteja na ThreadLocal, se não ouver uma conexão ou uma sessão, ele não faz nada.

Quem tem que abrir uma conexão ou iniciar uma sessão com o Hibernate são as classes que tem responsabilidade pelo banco de dados.

É simples, basta criar uma classe utilitária com duas ThreadLocal, uma pra sessão e outra pra transação. Sempre que alguém chamar o método de iniciar uma sessão, a classe verifica se nessa ThreadLocal tem alguma coisa, se tiver, ela retorna a session que já está aberta, se não tiver ela abre uma nova, simples.

Olha a que eu uso aqui:

/*
 * Created on 11/05/2005
 *
 * Código desenvolvido por Maurício Linhares
 * 
 */
package br.edu.cefetpb.eteacher.db;

import org.apache.log4j.Logger;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;

/**
 * @author Maurício
 *
 * Código desenvolvido por Maurício Linhares
 * 
 */
public class HibernateUtility {

    private static Logger logger = Logger.getLogger(HibernateUtility.class);
    
    private static final SessionFactory factory;
    private static final ThreadLocal threadSession = new ThreadLocal();
    private static final ThreadLocal threadTransaction = new ThreadLocal();
        
    static {
        factory = new Configuration().configure().buildSessionFactory();
    }
    
    public static Session getSession() {
        
        Session session = (Session) threadSession.get();
        
        if ( session == null || !session.isOpen() ) {
        
            session = factory.openSession();
            threadSession.set(session);
            logger.debug("Abriu a sessão");
            logger.debug(session);
            beginTransaction();
        }
        
        return session;
        
    }
    
    public static void closeSession() {
        
        Session session = (Session) threadSession.get();
        
        if (session != null && session.isOpen() ) {
            threadSession.set(null);
            session.close();
            logger.debug("Fechou a sessão");
            logger.debug(session);            
        }
        
    }
    
    public static void beginTransaction () {
        
        Transaction transaction = (Transaction) threadTransaction.get();
        
        if (transaction == null) {
            transaction = getSession().beginTransaction();
            threadTransaction.set(transaction);
            logger.debug("Iniciou a transação:");
            logger.debug(transaction);            
        }
        
    }
    
    public static void commitTransaction() {
        
        Transaction transaction = (Transaction) threadTransaction.get();
        
        if (transaction != null && !transaction.wasCommitted() && !transaction.wasRolledBack() ) {
            
            transaction.commit();
            logger.debug("Deu commit na transação:");
            logger.debug(transaction);                
            
        }
        
    }
    
    public static Session getFreeSession() {
        return factory.openSession();
    }
    
}
S

Por que um filtro não pode abrir uma sessão do hibernate e colocar no thread local para uma action que com certeza vai precisar dele ???

Vc sabia que abrir uma sessão no Hibernate tem custo muito baixo ??? (Não! O hibernate não pega uma conexao quando vc abre uma sessao!)

O próprio cara do Hibernate recomenda esse pattern, isto é, uma session por request. Não sou eu que estou falando. Tá escrito lá no tutorial do Hibernate.

S

Não é que JavaBeans esteja falido e seja uma má prática. Não sejamos tão intolerantes quanto a vida. Perfeição não existe! Eu acho chato perseguir a perfeição. Tudo na vida tem vantagens e desvantagens. Nada é perfeito.

JavaBeans é legal mas quebra a teoria de OO pois expões desnecessariamente seus atributos privados.

Não é nada que mate alguem, mas tem essa desvantagem.

E isso tb não é consenso. Alguns acham babaquice, outros acham totalmente relevante. É como quem é melhor Net ou Java. É uma discussão tola…

Quando eu falo em beans, não me refiro a JavaBeans, mas POJO.

O mais certo seria assim:

public class MyBean {

    @property(name=attribute1)
    private String attribute1;

    @property
    private String attribute2;

}

Who gives a shit?

C

Mauricio,

Vc poderia me explicar melhor como funcionam as propriedades do c# e como, no seu ver, eu substituo os javabeans no java? Qual é boa pratica com relacao a isso em java?

heheeh, cuidado com o que vcs falam, vcs abalaram grandemente as minhas bases de java! hahuahuauahu!

um abraco

R

No minimo questionavel essa afirmacao. Pq, no final das contas, vai virar get_Xxx e set_Xxx da mesma forma.

Mas nao ficou estranho mesmo assim? A classe A abre a conexao, e a B fecha?

Rafael

R

carneiro:
Mauricio,

Vc poderia me explicar melhor como funcionam as propriedades do c# e como, no seu ver, eu substituo os javabeans no java? Qual é boa pratica com relacao a isso em java?

heheeh, cuidado com o que vcs falam, vcs abalaram grandemente as minhas bases de java! hahuahuauahu!

um abraco

Propriedade nao tem nada a ver com “java beans”. Ao inves de fazer

carro.setPassageiros(5);
int num = carro.getPassageiros();

vc faz

carro.Passageiros = 5;
int num = carro.Passageiros;

mas no fundo eh tudo getter e setter mesmo.

E pq isso abalou as tuas bases de Java? Sao apenas maneiras ligeiramente diferentes de fazer a mesma coisa. O que muda eh o gosto de cada um.

Rafael

R

carneiro:
javaBeans é uma especificação falida de Sun

PC, por que uma especificação fálida?

Explique melhor, kra, porque eu vejo os javaBeans como extremamente úteis e como uma boa prática de programação.

Voce esta falando de pojos simplesmente. Eh o que o Java te proporciona, gostem ou nao.

Rafael

C

Certo, certo, só não entendi porque o Mauricio e o PCalcado disseram que não é boa prática fazer javabeans.

Sinceramente, não vi pq nao seja.

R

Ninguem falou que eh “ma pratica”. O Phillip apenas frizou que o melhor, atualmente, eh chamar de objeto de negocio, ao inves de “javabean”. O conceito original de “javabean” ja se perdeu no tempo. Hj em dia eh apenas uma classe java “simples”, “pura”…

Liberte a mente :slight_smile:

Rafael

S

Procurando no GUJ, encontrei esse post aqui: http://www.guj.com.br/posts/list/6833.java

A action abaixo implementa lógica ??? add, remove, alterar ???

Quero entender o limiar entre ter lógica na action e não ter lógica na action para decidir se volto para servlets puros ou não.

[color=red]Algúem poderia dar um exemplo do que seria uma action pecadora com lógica dentro e o que seria uma puritana sem lógica dentro ???[/color]

public class GerenciarUsuario extends ActionSupport implements UsuarioAware, UsuarioDAOAware {
 
   private Usuario usuario;
   private UsuarioDAO dao;
 
   public void setUsuario(Usuario u) {
     this.usuario = usuario;
   }
 
   public Usuario getUsuario() {
     return usuario;
   }
 
   public void setUsuarioDAO(UsuarioDAO dao) {
     this.dao = dao  }
 
   public UsuarioDAO getUsuarioDAO() {
     return dao;
   }
 
   public String adicionar() throws Throwable {
      try {
         dao.adicionar(usuario);
         return SUCCESS;
      } catch(DAOException e) {
         addError(getText(e.getMessage()));
         return ERROR;
      }
   }
 
   public String remover() throws Throwable {
      try {
         dao.remover(usuario);
         return SUCCESS;
      } catch(DAOException e) {
         addError(getText(e.getMessage()));
         return ERROR;
      }
   }
 
   public String alterar() throws Throwable {
      try {
         dao.alterar(usuario);
         return SUCCESS;
      } catch(DAOException e) {
         addError(getText(e.getMessage()));
         return ERROR;
      }
   }
 }
M

Eu não sei quem recomendou, mas no meu “Hibernate in Action” é assim. No filtro só tem “doFilter()”, “commitTransaction()” e “closeSession()”, nessa ordem. Ele não abre sessão no filtro.

Pois é, só que até ter uma conexão denovo, ela não serve pra nada além de manter o controle sobre os objetos persistentes.

Sei lá, no Spring é assim também, pra métodos transacionais que não exigem uma transação anterior e funciona. Eu também uso e funciona normalmente pra mim.

Rafael Steil:
Ninguem falou que eh “ma pratica”. O Phillip apenas frizou que o melhor, atualmente, eh chamar de objeto de negocio, ao inves de “javabean”. O conceito original de “javabean” ja se perdeu no tempo. Hj em dia eh apenas uma classe java “simples”, “pura”…

Liberte a mente :slight_smile:

Rafael

Disse tudo :mrgreen:

S

Quando eu falo bean (nunca falei javabean), é claro que é POJO e não JavaBean ou EJB.

Nego saiu pela tangente com o assunto de JavaBeans e confundiu a cabeça do Carneiro. (E provavelmente a minha tb…)

Vamos focar no assunto em questão que já tá polêmico demais…

S

Link: http://www.hibernate.org/hib_docs/v3/reference/en/html/transactions.html

e mais importante:

M

saoj:
A action abaixo implementa lógica ??? add, remove, alterar ???

Quero entender o limiar entre ter lógica na action e não ter lógica na action para decidir se volto para servlets puros ou não.

Algúem poderia dar um exemplo do que seria uma action pecadora com lógica dentro e o que seria uma puritana sem lógica dentro ???

Pense no seguinte, se não fosse no Mentawai, ou pior ainda, se não fosse web, a lógica funcionava?

M

saoj:
Link: http://www.hibernate.org/hib_docs/v3/reference/en/html/transactions.html

e mais importante:

Pronto, então no Hibernate vai dar no mesmo. Mas eu prefiro não abrir :lol:

S

Maurício Linhares:

Pense no seguinte, se não fosse no Mentawai, ou pior ainda, se não fosse web, a lógica funcionava?

Fiquei na mesma, Maurício.

A action que eu postei é do WebWork.

Qualquer lógica vai funcionar em qualquer caso, desde que vc dê a ela o que ela necessita.

Acho que eu entendi a vantagem de não usar o Input e sim Injection: aquela action não vai depender do input do mentawai.

Mas o que eu realmente não entendo é: a porcaria da action não extends ActionSupport não já tá atrelada ao framework de qualquer jeito ??? Esse negócio de fazer as coisas totalmente independentes de tudo, que podem ser migradas pra tudo que é canto não está com nada.

Quem realmente precisa disso ??? Quem realmente precisa pegar sua action e colocar para rodar em Swing ???

Se vc quer fazer um front-end com swing para o seu sistema, faça requisições HTTP para o seu servidor e apenas exiba os resultados num componente Swing. :? E seja feliz !!! :lol:

[color=red]To começando a achar que eu viajei. Eu nunca quis botar lógica dentro da minha action. Quer dizer, fiquei tentando no caso do LoginAction de acessar o DB diretamente, mas do resto eu acho que é pra fazer como essa action do webwork que eu postei. E usar o bom senso tb. Do jeito que vcs falam parece que é proibido qualquer IF ali dentro. :lol: [/color]

M

saoj:
Mas o que eu realmente não entendo é: a porcaria da action não extends ActionSupport não já tá atrelada ao framework de qualquer jeito ??? Esse negócio de fazer as coisas totalmente independentes de tudo, que podem ser migradas pra tudo que é canto não está com nada.

Quem realmente precisa disso ??? Quem realmente precisa pegar sua action e colocar para rodar em Swing ???

Ave maria, calma! :lol:

A Action vai pro lixo Sérgio, não me interessa reaproveitar a Action, o que me interessa é reaproveitar a lógica de negócio, que é ver se o usuário está cadastrado e logar ele no sistema, colocando ele na “coleçãoDeUsuáriosLogados”. A Action é só um objeto Command e isso agente nunca reaproveita. Ou alguém já reaproveitou?

Eu vi um pessoal desenvolvendo uma aplicação de automação comercial onde era muito importante que ela fosse tanto web quanto desktop, então eles separaram a lógica assim. Ela havia sido feita pra web com Struts, no desktop era Swing. Os actions do Struts só chamavam as classes de negócio, toda a lógica, acesso a bancos de dados e o resto das coisas eram feitos fora do Struts, no fim, ele reaproveitaram praticamente tudo da versão web.

S

Desulpe o nervosismo gerado pela minha ignorância.

Estou mais calmo agora, pois acho que viajei. Não preciso voltar para o servlet puro, pois as actions podem ser mais que simples controlodoras burras.

Eu nunca quis botar lógica dentro da minha action. Quer dizer, fiquei tentando no caso do LoginAction de acessar o DB diretamento, confesso! Mas do resto eu acho que é pra fazer como essa action do webwork que eu postei. E usar o bom senso tb. Do jeito que vcs falam parece que é proibido qualquer IF ali dentro.

Vcs tem razão: acesso ao banco dentro da action é feio. ConnectionFilter foi um erro pois passa a impressão que usar a Connection dentro da action é legal. Acredito que mudar o filtro para ele adicionar a connection (ou a session do hibernate) no thread local resolve tudo. (Espero!!! Se não resolver falem! :slight_smile: )

P

O que eu vou falar agora ou vai ajudar a esclarecer ou vai f%$&er tudo de vez :mrgreen:

Existe outra divisão em um sistema. A divisão de domínios.

Domínio Fundamental
Classe fundamentais, utilizadas em diversos sistemas.
Integer, Boolean, String, BigDecimal…

Domínio de Arquitetura
Um pouco mais alto nível lida com coisas relativas á arquiteura da aplicação. Reutilizáveis por diversas aplicações em diversas indústrias. Incluem classes para conexão entre máquinas, acesso a base de dados, interfaces…
RMI, JDBC, HttpServlet…

Domínio de Negócios
Classes específicas do negócio. Contêm os relacionamentos e regras de negócio do domínio em questão. O Domain Model. Pode ser utilizado para construir diversas aplicações.
Cliente, Venda, Pedido, ClienteDAO etc.

Domínio de Aplicação
Classes e componentes relativos à uma aplicação. Gerenciadores de eventos que sabem responder a estímulos. Sabem que se o usuário submeter o link XYZ ou clicar no botão ABC é para informar ao domínio de baixo sobre a ciração de um novo pedido. Não são reutilizados fora da aplicação.
Action, GerenciadorClientes, JSPs, AdicionarClienteServlets…

Resusabilidade, do pior para o melhor:
Aplicação -> Negócios -> Arquitetura -> Fundamental

Vejamos. É importante, então, isolarmos as regras de negócio no domínio de negócios, porque estas são reutilizáveis. (e flexíveis). A aplicação nem tanto.

Existe quem goste de colocar todo o domínio de aplicação na camada web, com um framework MVC qualquer. Isso abaixa ainda mais a reusabilidade da aplicação, mas essa já não era alta. Geralmente se você precisa reutilizar regras da aplicação, é porque estas regras são do negocio, não da aplicação. Refatore.

Eu pessoalmente gosto de colocar o mínimo possível na interface e criar classes Gerenciadores (estou querendo me livrar disso, mas aidna não achei solução melhor) que possuem métodos para realizar as etapas (adicionaUsuario(Usuario u), etc.). Assim a reusabilidade é maior, crieo, e muitas vezes já fiz aplicações com mais de uma interface (mais de cinco até!) para a mesma regra de negócios.

P

Ah, para mais sobre o assunto, leiam esse livro :wink:

T

Olá! (òtimo tópico hein!)

Concordo, e assim basta usar o junit para mim testar minhas actions. Não precisaremos de mocks implementando Input ou Output. Apenas precisaremos criar mocks para classes de negócios ou de persistências da aplicação que ainda não foram desenvolvidos, ou para garantir maior performance durantes a execução dos testes unitários!

Um sistema bém testado (suponho que com este post vc quer atingir programadores que saibam testar e desenvolver com qualidade), é bém provavel que o código estará bém desacoplado e as lógicas de negócio estaráo nos seus devidos lugares. De certa forma, para que seja fácil e produtivo testar, as divisões de responsabilidades do sistema deverão estar bém definidas.

Sendo assim, se precisar mover minha app de web para swing, um refactoring é mais que suficiente. Meus testes vão me dar o feedback que preciso para ter certeza que o refactoring está acontecendo com sucesso!

Ainda prefiro o refactoring! Já que é uma apicação swing, preferiria poupar este processamento e buscar algo mais ágil. :wink:

Bom, minha opinião.
Abraços!
Thiago Senna

D

Eu posso estar viajando no que vou dizer a seguir, mas depois de ler este tópico e outros (principalmente sobre testes unitários) minha cerebro está quase derretendo. :oops:

Uma action é o lugar onde devemos colocar a lógica de controle, então ela não deve realizar outras tarefas e por isso os seus teste unitários devem conter apenas teste de lógica.

saoj:
...Mas do resto eu acho que é pra fazer como essa action do webwork que eu postei...

Essa action é simples, mas mesmo assim e se tivermos de fazer coisas com autenticar o usuário antes da requisição, verificando se ele tem permissão para aquela ação, ou registar aquela ação em um BD, como seria o teste? E nossa action não passaria a depender de outros DAOs? :shock:

Eu andei dando uma olhada no pattern Chain of Responsibility que pode ser facilmente implementado com essa biblioteca da Jakarta. Vejam um exemplo:

public class GerenciarUsuario extends ActionSupport {

    public String adicionaUsuario = "AdicionarUsuario";
    
    public String adicionar() throws Throwable {

        try {

            Command chain = ChainFactory.getChain(adicionaUsuario);
            ActionContext context = new ActionContext();
            context.put("usuario", usuario);
            
            if (chain.execute(context)) {
                return ERROR;
            }
            return SUCESS;

        } catch (Exception e) {
            addError(getText(e.getMessage()));
            return ERROR;
        }
    }
}

Onde podemos, na classe que irá implementar os testes unitários, redefinir a string da variável adicionarUsuario da action por algo do tipo "AdicionaUsuarioMock" (não consegui imaginar uma forma melhor de fazer isso :oops: ), facilitando a criação e a chamada da classe Mock que irá simular o comportamento da série de commands que teriamos que chamar.

Desta forma eu acredito que teremos uma maior reusabilidade dos objetos da camada do "Domínio de Negócios" e um baixo acoplamento entre eles e a action, através de classes que implementem o pattern Command e ficariam responsáveis por chamar os objetos. Além de facilitar imensamente a alteração (inclusive da ordem de chamada), inclusão e exclusão desses "commands".

Outra coisa que me deixa com um :?: na cabeça. Os testes unitários devem testar a funcionalidade de uma classe específica, e por isso criamos essas classes Mocks para simular as funcionalidades das classes que ela depende. Agora se criassemos Mocks para os DAOs da action acima, como saberiamos que eles estariam acessando corretamente a informação? Tudo bem, quem é responsável por isso é o DAO e não a action. Mas e o DAO, se substituirmos o SGBD por uma classe Mock, como teremos certeza que o mapeamento XML (como no caso do Hibernate), ou as querys estão corretas. Não seria correto testar diretamente com o BD, como disse o Sérgio, ou isso seria testes de integração?

Estou cada vez mais fascinado por este novo mundo, cercado de patterns e testes por todos os lados :D . Só que não consigo para de pensar se, por empolgação, não estou "matando mosca com tiro de canhão" :mrgreen: e fazendo mau uso desses padrões, quase como uma orverdose.

Será que vocês, gurus aqui do GUJ poderiam me dizer se pelo menos estou no caminho certo e conseguindo entender o que vocês estão dizendo aqui no Forum.

Abraços

S

Várias coisas que eu vou pesquisar:

:arrow: O JMock facilita a criação de mocks pra tudo, desde que vc programe seu model de negócios em cima de interfaces e com IoC, isto é, essas interfaces do model são injetadas de alguma maneira nas suas actions. (É isso Phillip?)

:arrow: Model-driven do WW. Te permite dar um bypass na action injetando o input direto no model de negócios.

:arrow: Quando for testar os DAOs, acho que vc precisa testar eles com o banco de dados. Nem que seja um banco de testes igual ao de produção. Se não é o que vc falou…

:arrow: Chain of Resonsability parece legal, mas se o JMock resolve o problema dos mocks, então prefiro trabalhar com mais liberdade usando interfaces mesmo.

Eu acho que isso deve estar em filtros, não na action.

R

debersom:

Eu andei dando uma olhada no pattern Chain of Responsibility que pode ser facilmente implementado com essa biblioteca da Jakarta. Vejam um exemplo:

Posto dessa forma nao tem muita vantagem, ja que vc estara simplesmente criando um passo a mais ate chegar a acao de fato. Uma maneira melhor seria fazer a action ja acessar os services.

debersom:

Outra coisa que me deixa com um :?: na cabeça. Os testes unitários devem testar a funcionalidade de uma classe específica,
[ … ]
Mas e o DAO, se substituirmos o SGBD por uma classe Mock, como teremos certeza que o mapeamento XML (como no caso do Hibernate), ou as querys estão corretas.

Voce nao vai poder usar mock - vai ter que ir ao banco de dados mesmo.

Rafael

T

Sim, os mock dinâmicos em geral facilita a criação de qualquer coisa. Mas na minha opinião quando eu estiver testando uma Action no Mentawai, eu quero criar os mocks para os DAO’s e outras classes que a Action usa. Eu não quero um mock para minha própria action.

Quando faço os testes do DAO, eu testo ele acessando o banco. Por natureza, de acordo os testes vão crescendo, a tendência é eles demorarem mais em comparação com outros testes.

Eu tenho o habito de criar um teste suite para o teste dos DAO’s. Assim quando eu estiver testando outras áreas do sistema, eu consigo tem testes mais rápido.

Se durante os testes das actions eu ficar acessando os DAO’s que possuem acesso ao banco, o teste das actions também ficarão lentos. Por isso eu utilizo mocks dinâmicos para implementar as interfaces dos DAO’s quando estou testando minhas actions, já que aqui meu objetivo não é testar os dao’s, já que eles estão sendo testados em outro lugar!

P

Oi,

O Jmock facilita a criação de mocks e asserção. Não é o único, mas eu tenho usado bastante.

Você não precisa ter apenas interfaces, basta usar o pacote opcional da CGLIB.

Sobre testes, aquele livro chato do pressman tem muita coisa. Outros também, claro, tire aquele livro empoeirado de Engenharia de Software da estante.

S

Sim !!! Vc cria mocks pro model (DAO, POJOs, etc) que está dentro da action, não para a própria action. Se não vc vai testar o mock e não a action !!! :lol:

Thiago, quando vc fala que vc não quer usar o MockInput e o MockOutput, eu acho estranho, pois vc não vai ter que usar um monte de mocks de qualquer jeito para o model ??? Esses mocks já estão prontos e vc vai usá-los muito facilmente, sem qualquer dor de cabeça.

Mas isso é gosto! Vc pode preferir trabalhar com injection e ignorar o INPUT. Só não consigo entender quando vc falar: “para facilitar os testes…”

T

saoj:
Thiago, quando vc fala que vc não quer usar o MockInput e o MockOutput, eu acho estranho, pois vc não vai ter que usar um monte de mocks de qualquer jeito para o model ??? Esses mocks já estão prontos e vc vai usá-los muito facilmente, sem qualquer dor de cabeça.

saoj…

Se o meu objetivo e ter minhas actions bém testadas, não irá fazer diferença eu usar MockHttpServletRequest ou MockInput.

Se quando eu for fazer um teste eu tiver que me preocupar e criar e instaciar o MockInput, MockOutput, MockClienteDao para ai sim eu poder testar minha action, eu não vejo vantagem nisso!
Desta forma, toda action que eu criar terei que criar o MockInput, o MockOutput e o mock para o dao que eu tiver usando. Assim, vai ficar muito chato testar minhas actions e demorado, já que para action eu terei com certeza mais de um teste!

Se você eliminar a necessidade de usar estes MockInput e MockOutput, assim como no webwork, os meus testes serão mais legíveis e mais fáceis de implementar!

Já que testes são usados em metodologias ágeis, quanto mais vc facilitar a criação dos testes, mais você vai agradar este público alvo! :wink:

Bom… quando digo facilitar os testes, o que escrevi a cima de certa forma já responde essa questão.
Suponde que para uma action eu escreva 3 testes. Logo, terei 3 MockInput, 3 Mock Output e 3 DAOMock… Daí, obrigatoriamente teria que pensar em um refactoring para minimizar toda esta repetição.

Se vc eliminar eles MocksInput e Output, eu me preocuparia apenas com os Mocks dos meus DAO’S!

Abraços!
Thiago Senna

T

nossa… o q eu fiz ai em cima!!! uau… bug, bug, bug,bug!!! :mrgreen:

Rafael Steil, tu viu o que eu tinha feito?

eu fiz o seguinte… na mensagem acima, no primeiro quote que eu coloquei, eu esqueci de colocar o “]’” depois de "["quote=saoj…

A tela virou mau salada!

M

Ora, qual é a responsabilidade do DAO? Abstrair o lugar onde os dados vão ser persistidos. Então o que é que tem que ser testado? Se ele está persistindo os dados corretamente no mecanismo de persistência, tem que testar com o banco de dados, a serialização ou sejá lá como a coisa estiver sendo persistida. Tem um material interessante sobre isso aqui:

http://www.theserverside.com/articles/article.tss?l=UnitTesting

Exatamente :mrgreen:

Criado 16 de julho de 2005
Ultima resposta 17 de jul. de 2005
Respostas 43
Participantes 7