Problema com Hibernate Validator e VRaptor 3.2.0

15 respostas
leandros

Pessoal estou com um problema estranho na hora da validação de um objeto que tem um atributo BigDecimal.
Ele está anotado da seguinte maneira.

@NotNull(message="{product.price.null}")
	@DecimalMin(value="0", message="{product.price.min}")
	@Column(nullable=false)
	private BigDecimal price;

Quando eu executo os testes unitários com esse objeto, ele valida certinho e mostra a mensagem de erro correta. Porém quando eu executo meus testes de controller, onde eu faço a chamada do validator.validate(foo); ele não valida esse atributo. Ele deveria lançar uma ValidationException e não lança. O que pode ser, alguém ja passou por isso? No meu teste eu seto price com um valor negativo para verificar sua integridade e ele não lança uma ValidationException.

15 Respostas

Lucas_Cavalcanti

vc usa o MockValidator nos testes? ele não faz nada no método validate…

o que eu costumo fazer nos testes do controller é forçar o erro no método validate… por exemplo usando mockito:

Validator validator = spy(new MockValidator());
...
doThrow(new ValidationException()).when(validator).validate(meuobjeto);
...
controller.logica(meuobjeto);
leandros

Puxa verdade, vi no source code!! Implementei um MockValidator que valida com hibernate validator, e verifica minhas mensagens de erro. Valeu Lucas.

A

Lucas Cavalcanti:
vc usa o MockValidator nos testes? ele não faz nada no método validate…

o que eu costumo fazer nos testes do controller é forçar o erro no método validate… por exemplo usando mockito:

Validator validator = spy(new MockValidator()); ... doThrow(new ValidationException()).when(validator).validate(meuobjeto); ... controller.logica(meuobjeto);

Estou tentando rodar os testes do projeto mydvds. Alterei a classe UsersController no metodo add para usar a validação do hibernate.
No UsersControllerTest metodo addingAUserWithHibernateValidationErrors coloquei o codigo sugerido pelo Lucas, mas não está compilando. Não encontra o new ValidationException()
em qual pacote ele está?

Ou como posso fazer a implementação tal qual o leandros?

@Test(expected=ValidationException.class)
	public void addingAUserWithHibernateValidationErrors() throws Exception {
		User user = new User();
		user.setLogin("myLogin");
		user.setPassword("short");
		user.setName("Testing");

		ifTheUserWithThisLoginDoesntExist("myLogin");
		willNotAddTheUser(user);

            doThrow(new ValidationException()).when(validator).validate(user);
		controller.add(user);
	}
Lucas_Cavalcanti

olá admwagner,

o ValidationException é do pacote br.com.caelum.vraptor, mas ele não tem construtor vazio, vc precisa passar algo pra ele, tipo uma string:

new ValidationException("abc")

pra fazer como o leandros, é só criar a classe:

public class MeuMockValidator extends MockValidator {
    
    @Override
    public void validate(Object objeto) {
         this.addAll(Hibernate.validate(objeto));
    }
}

e usá-la como MockValidator

A

Lucas,
Não nao rodou/funcionou com o hibernate validator 4, apenas com o 3. O metoto Hibernate.validate está @Deprecated
alguma sugestão?

Nos projetos que vc trabalha tem usado a validação do hibernate ou tem feito na mão no controller?

Testcase: AdicionaComNomeNulo(br.com.admwagner.processos.controller.CadastroGeralControllerTest):        Caused an ERROR
Unexpected exception, expected<br.com.caelum.vraptor.validator.ValidationException> but was<java.lang.NoClassDefFoundError>
java.lang.Exception: Unexpected exception, expected<br.com.caelum.vraptor.validator.ValidationException> but was<java.lang.NoClassDefFoundError>
Caused by: java.lang.NoClassDefFoundError: org/hibernate/validator/ClassValidator
        at br.com.caelum.vraptor.validator.ValidatorLocator.getValidator(ValidatorLocator.java:47)
        at br.com.caelum.vraptor.validator.ValidatorLocator.getValidator(ValidatorLocator.java:47)
        at br.com.caelum.vraptor.validator.Hibernate.validate(Hibernate.java:40)
        at 
Test br.com.admwagner.processos.controller.CadastroGeralControllerTest FAILED
/dext3/home-java/processos/nbproject/build-impl.xml:1016: Some tests failed; see details above.
FALHA NA CONSTRUÇÃO (tempo total: 4 segundos)
Lucas_Cavalcanti

o ideal é usar o new HibernateValidator3(localization).validate(object);

vc pode receber o HibernateValidator3 no construtor ou simplesmente receber o localization para repassar.

Lucas_Cavalcanti

sempre que possível a gente usa o hibernate validator, principalmente se for validação do domínio.

A

Lucas Cavalcanti:
o ideal é usar o new HibernateValidator3(localization).validate(object);

vc pode receber o HibernateValidator3 no construtor ou simplesmente receber o localization para repassar.

Isto diretamente no teste? ou criando o MeuMockValidator.
A classe para o hibernate validator 4 é JSR303Validator?

Lucas_Cavalcanti

criando o SeuMockValidator.

sim, a do HV4 é essa mesmo

leandros

Eu fiz como na documentação do Hibernate Validator, não sei se é a melhor forma!! E adicionei outro método utilitário para verificar se a mensagem de erro que eu quero existe na collection errors. Segue meu MockValidator.

package br.com.witvision;

import java.util.Set;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;

import br.com.caelum.vraptor.validator.Message;
import br.com.caelum.vraptor.validator.ValidationMessage;

public class MockValidator extends br.com.caelum.vraptor.util.test.MockValidator {
	
	private Validator validator;
	
	public MockValidator() {
		validator = Validation.buildDefaultValidatorFactory().getValidator();
	}
	
	@Override
	public void validate(Object object) {
		Set<ConstraintViolation<Object>> constraints = validator.validate(object);
		for (ConstraintViolation<Object> constraintViolation : constraints) {
			add(new ValidationMessage(constraintViolation.getMessage(), constraintViolation.getMessageTemplate().replaceAll("[{}]", "")));
		}
	}
	
	public boolean hasError(String error) {
		boolean has = false;
		for (Message message : getErrors()) {
			System.out.println(message.getMessage());
			if (message.getMessage().equals(error)) has = true;
		}
		return has;
	}

}

Então você pode testar seu domínio usando Hibernate Validator, e testar as mensagens de erro que tem que aparecer nos testes colocando um assertTrue(validator.hasError(“Nome é obrigatório”)) por exemplo.

A

Abaixo minha singela contribuição.

  1. Atualizei o sistema para o hibernate-distribution-3.6.0.Final
  2. Atualizei o sistema para o hibernate-validator-4.1.0.Final
  3. Removi a biblioteca ejb3-persistence-1.0.1.GA.jar ==> estava gerando o seguinte erro:
------------- ---------------- ---------------
Testcase: AdicionaComNomeNulo(br.com.admwagner.processos.controller.CadastroGeralControllerTest):        Caused an ERROR
javax.persistence.Persistence.getPersistenceUtil()Ljavax/persistence/PersistenceUtil;
java.lang.NoSuchMethodError: javax.persistence.Persistence.getPersistenceUtil()Ljavax/persistence/PersistenceUtil;
        at org.hibernate.validator.engine.resolver.JPATraversableResolver.isReachable(JPATraversableResolver.java:62)
        at org.hibernate.
Test br.com.admwagner.processos.controller.CadastroGeralControllerTest FAILED
/dext3/home-java/processos/nbproject/build-impl.xml:1024: Some tests failed; see details above.
FALHA NA CONSTRUÇÃO (tempo total: 9 segundos)

Criei a seguinte Classe em um pacote utilitario:

package br.com.admwagner.util;

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
import br.com.caelum.vraptor.util.test.MockValidator;
import java.util.Set;

import javax.validation.ConstraintViolation;
import javax.validation.Validator;
import org.apache.log4j.Logger;
import br.com.caelum.vraptor.validator.ValidationMessage;
import br.com.caelum.vraptor.validator.ValidationException;
import javax.validation.Validation;

/**
 *
 * @author [email removido]
 */
public class MockValidatorHibernate extends MockValidator {

   private Validator validator;
   private static final Logger log = Logger.getLogger(MockValidatorHibernate.class);

   public MockValidatorHibernate() {
      validator = Validation.buildDefaultValidatorFactory().getValidator();
   }


   @Override
     public void validate(Object bean) {

      log.debug("------------------------------------------ inicio validate ");
      final Set&lt;ConstraintViolation&gt;&lt;Object&gt;&gt; violations = validator.validate(bean);
      
      for (ConstraintViolation&lt;Object&gt; constraintViolation : violations) {
         
         add(new ValidationMessage(constraintViolation.getMessage(), constraintViolation.getMessageTemplate().replaceAll("[{}]", "")));
         
      }

      if (!getErrors().isEmpty()) {
         log.debug("---------" + getErrors().size() + " mensagens de erros foram identificadas!! - ATENÇÃO!");
         // se encontrou erros na validação dispara a Exception que será verificada no test
         throw new ValidationException(getErrors());
      }

   }

}

Classe de teste

package br.com.admwagner.processos.controller;


import java.util.List;
import org.apache.log4j.Logger;
import br.com.caelum.vraptor.util.test.MockResult;
import br.com.admwagner.processos.dao.CadastroGeralDao; // importando o dao real (não usei o mock ainda)
import br.com.admwagner.processos.model.CadastroGeral;
import br.com.admwagner.util.MockValidatorHibernate;    // Mock ora implementado.
import br.com.caelum.vraptor.validator.ValidationException;

import org.hibernate.SessionFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.hibernate.cfg.Configuration;
import org.hibernate.classic.Session;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue;


/**
 *
 * @author admwagner
 */
public class CadastroGeralControllerTest {
   private static final Logger log = Logger.getLogger(CadastroGeralControllerTest.class);
      private Session session;
   private CadastroGeralDao dao;
   private MockResult result;
   private CadastroGeralController controller;
private MockValidatorHibernate validator;

    public CadastroGeralControllerTest() {
    }

    @Before
	public void setUp() throws Exception {
      Configuration cfg = new Configuration();
	cfg.configure();
      SessionFactory factory = cfg.buildSessionFactory();
      session = factory.openSession();
	session.beginTransaction();
      
	dao = new CadastroGeralDao(session);

      result = new MockResult();
       validator = new MockValidatorHibernate();

      controller = new CadastroGeralController(dao, validator, result);
   }


    @After
   public void tearDown() throws Exception {
      if (session != null && session.getTransaction().isActive()) {
         session.getTransaction().rollback();
      }
    }

   @Test
   public void AdicionarNovoCadastroSemErros(){
      CadastroGeral cad = new CadastroGeral("teste nome", "[telefone removido]");
      controller.adiciona(cad);

      String resultado;
      resultado = result.included("alerta");
      assertTrue(resultado.length()&gt;0);
      assertTrue(dao.existeCadastroPorCpfCnpj("[telefone removido]"));


   }

    @Test(expected=ValidationException.class) //  se houve erro na validação a classe MockValidatorHibernate irá gerar uma Exception
   public void AdicionaCadastroComNomeNuloGeraErro(){
      CadastroGeral cad = new CadastroGeral( );
      cad.setCpfcnpj("[telefone removido]");
      controller.adiciona(cad);
   }

}

O Model:

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package br.com.admwagner.processos.model;

import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import org.hibernate.annotations.GenericGenerator;
import javax.persistence.Id;
import org.hibernate.validator.constraints.NotEmpty;






/**
 *
 * @author admwagner
 */
@Entity
public class CadastroGeral implements Serializable {
    private static final long serialVersionUID = 5969734540775236852L;

    @Id
    @GeneratedValue(generator = "system-uuid")
    @GenericGenerator(name = "system-uuid", strategy = "uuid")
    private String id;

    @NotEmpty(message="item obrigatorio")
    private String nome;


    private String cpfcnpj;

    public CadastroGeral() {    }

    public CadastroGeral(String id, String nome, String cpfcnpj) {
        this.id = id;
        this.nome = nome;
        this.cpfcnpj = cpfcnpj;
    }
    public CadastroGeral(String nome, String cpfcnpj) {
        this.nome = nome;
        this.cpfcnpj = cpfcnpj;
    }
        public CadastroGeral(String nome) {
        this.nome = nome;
    }
    

    public String getCpfcnpj() {
        return cpfcnpj;
    }

    public void setCpfcnpj(String cpfcnpj) {
        this.cpfcnpj = cpfcnpj;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getNome() {
        return nome;
    }

    public void setNome(String nome) {
        this.nome = nome;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final CadastroGeral other = (CadastroGeral) obj;
        if ((this.id == null) ? (other.id != null) : !this.id.equals(other.id)) {
            return false;
        }
        if ((this.nome == null) ? (other.nome != null) : !this.nome.equals(other.nome)) {
            return false;
        }
        if ((this.cpfcnpj == null) ? (other.cpfcnpj != null) : !this.cpfcnpj.equals(other.cpfcnpj)) {
            return false;
        }
        return true;
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 79 * hash + (this.id != null ? this.id.hashCode() : 0);
        hash = 79 * hash + (this.nome != null ? this.nome.hashCode() : 0);
        hash = 79 * hash + (this.cpfcnpj != null ? this.cpfcnpj.hashCode() : 0);
        return hash;
    }


    

}

O controller:

package br.com.admwagner.processos.controller;

import br.com.admwagner.processos.dao.CadastroGeralDao;
import br.com.admwagner.processos.model.CadastroGeral;
import br.com.caelum.vraptor.Get;
import br.com.caelum.vraptor.Path;
import br.com.caelum.vraptor.Post;
import br.com.caelum.vraptor.Resource;
import br.com.caelum.vraptor.Result;
import br.com.caelum.vraptor.Validator;
import java.util.List;


import org.apache.log4j.Logger;

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/**
 *
 * @author admwagner
 */
@Resource
public class CadastroGeralController {
   private CadastroGeralDao dao;
   private Validator validator;
   private Result result;

   //private static final Logger log = Logger.getLogger(CadastroGeralController.class);

   public CadastroGeralController(CadastroGeralDao dao, Validator validator, Result result ) {
      this.dao = dao;
      this.validator = validator;
      this.result = result;
   }

   @Path("/cadastrogeral")
   @Get
   public void lista() { 
      
      List&lt;CadastroGeral&gt; listaCad = this.dao.listAll();
      result.include("lista", listaCad);
    }

   @Path("/cadastrogeral")
   @Post
   public void adiciona(final CadastroGeral cadastro) {
      validator.validate(cadastro);
      
      validator.onErrorUsePageOf(this).formulario();

      this.dao.saveOrUpdate(cadastro);

      result.include("alerta", "Cadastro " + cadastro.getNome() + " adicionado com sucesso.");
      result.redirectTo(this).lista();
    }

   @Path("/cadastrogeral/novo")
   @Get
   public void formulario() {}

}

Testes ok…

Obrigado ao Lucas e ao leandros pela atenção!

Talves fosse interessante incluir este mock no VRaptor…

Lucas_Cavalcanti

cria um fork e adiciona isso por favor?

abraços

A

não sei usar o github… sou apenas um aprendiz…

instalei o git no arch-linux
Crie a conta admwagner, a chave publica, etc no github
Criei um fork do vraptor no github para a minha conta
criei, no meu pc, a pasta home-git e dentro dela dei o comando:

git init

Baixei para o pc local em uma pasta home-git (vraptor-core, vraptor-mydvds, etc) com o comando:
git clone --progress [email removido]:admwagner/vraptor.git

Parei… não sei o que fazer a partir daqui…

Lucas_Cavalcanti

agora é entrar na pasta vraptor que ele criou e modificar os arquivos

links úteis:
http://vidageek.net/2009/06/08/como-migrar-de-svn-para-git/
http://vidageek.net/2009/07/06/git-workflow/

A

vou tentar. se não conseguir crio uma nova mensagem no forum sobre o git e github

t+

Criado 3 de janeiro de 2011
Ultima resposta 23 de jan. de 2011
Respostas 15
Participantes 3