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.
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.
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 ???
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.
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.
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
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!
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?
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.
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
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 ???
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
[/quote]
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.
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.
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!)
Action é camada de controler. Pode ser MODEL mas não deve. Deve delegar a beans de negócio e/ou DAOs.
Criar uma action totalmente desacoplada do framework não dá. Cada Framework tem sua action.
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.
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.
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.
Então o ConnectionFilter ficou sem sentido. E pior que ele já está lá a um mes e ninguem falou anda.
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á)…
Err…uhm…na verdade, eu tive um papo com o Azenha sobre isso esse dias, quando ele me mostrou o filter do hibernate
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…
[quote=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.[/quote]
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();
}
}
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.
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;
}
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!
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[/quote]
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.