Duvida - injeção EntityManager

Pessoal como eu poderia testar um método (searchAll() do ClientManager - que é um EJB) , sendo que o método (searchAll) depende do entityManager que é injetado pelo JBoss Seam.

Obs.: Uso o Junit

Grata!

Eu acho bem pouco provavel q vc de fato precise testar tal método. Isso pq, se o método for tão simples como nome sugere, testa-lo pode ñ te trazer nenhum benefício prático. Teste apenas o q pode quebrar ou mudar na sua aplicação. De qualquer forma o q vc precisa é implementar um teste de integração. Esses testes servem p/ testar as fronteira do aplicativo, no caso a fronteira entra o aplicativo e a base de dados. A melhor forma de se fazer isso é com um banco de dados com dados controlados. Crie um EntityManager como vc o faria em um aplicativo JSE e use um construtor alternativo na sua classe ou deixe o atributo EntityManager em; packate private.

@Stateless
public class ClientManager {
  @PersistenceContext EntityManager em;

  public Client searchAll() {
    //...
  }
}

E no seu teste:

public class ClientManagerTest {
  EntityManager em;
  ClientManager cm;

  @BeforeClass
  public static void setUpClass() {
    Map<String, Object> override = new HashMap<String, Object>();
    override.put("javax.persistence.transactionType", "RESOURCE_LOCAL");
    override.put("javax.persistence.jdbc.driver", "driver da base de testes");
    override.put("javax.persistence.jdbc.url", "url da base de testes");
    override.put("javax.persistence.jdbc.user", "user da base de testes");
    override.put("javax.persistence.jdbc.password", "password da base de testes");

    em = Persistence.createEntityManagerFactory("MyPU", override).createEntityManager();
  }

  @Before
  public void setUp() {
    ClientManager em = new ClientManager();
    cm.em = em;

    em.getTransaction().begin();
  }

  @After
  public void tearDown() {
    em.getTransaction().rollback();
  }

  @Test
  public void testSearchAll() {
  }
}

Lembre-se sempre de ñ ficar alterando os dados da base de testes senão vc vai ter problemas p/ manter os testes.

[quote=dev.rafael]Eu acho bem pouco provavel q vc de fato precise testar tal método. Isso pq, se o método for tão simples como nome sugere, testa-lo pode ñ te trazer nenhum benefício prático. Teste apenas o q pode quebrar ou mudar na sua aplicação. De qualquer forma o q vc precisa é implementar um teste de integração. Esses testes servem p/ testar as fronteira do aplicativo, no caso a fronteira entra o aplicativo e a base de dados. A melhor forma de se fazer isso é com um banco de dados com dados controlados. Crie um EntityManager como vc o faria em um aplicativo JSE e use um construtor alternativo na sua classe ou deixe o atributo EntityManager em; packate private.

@Stateless
public class ClientManager {
  @PersistenceContext EntityManager em;

  public Client searchAll() {
    //...
  }
}

E no seu teste:

public class ClientManagerTest {
  EntityManager em;
  ClientManager cm;

  @BeforeClass
  public static void setUpClass() {
    Map<String, Object> override = new HashMap<String, Object>();
    override.put("javax.persistence.transactionType", "RESOURCE_LOCAL");
    override.put("javax.persistence.jdbc.driver", "driver da base de testes");
    override.put("javax.persistence.jdbc.url", "url da base de testes");
    override.put("javax.persistence.jdbc.user", "user da base de testes");
    override.put("javax.persistence.jdbc.password", "password da base de testes");

    em = Persistence.createEntityManagerFactory("MyPU", override).createEntityManager();
  }

  @Before
  public void setUp() {
    ClientManager em = new ClientManager();
    cm.em = em;

    em.getTransaction().begin();
  }

  @After
  public void tearDown() {
    em.getTransaction().rollback();
  }

  @Test
  public void testSearchAll() {
  }
}

Lembre-se sempre de ñ ficar alterando os dados da base de testes senão vc vai ter problemas p/ manter os testes.[/quote]

Ola dev.rafael ! Obrigada pela disposição em ajudar.

Realmente esse metodo é bem simples, mais há metodos bem complexos que obrigatoriamente precisam ser testados. Posso mostrar pelo menos um metodo para vc me auxiliar?

Izaura.

Poste ai q eu verei o q posso fazer.

Aqui esta!

class ClientManager:

[code]@Stateful(mappedName = “ejb/ClientManager”)
@Name(“clientManager”)
@Local(OrganizationManagerInterface.class)
@Remote(OrganizationManagerInterface.class)
@Scope(ScopeType.CONVERSATION)
public class ClientManager implements OrganizationManagerInterface {

   ...

@In
private EntityManager entityManager;

@In
private FacesMessages facesMessages;

  ...

public void save() { //obs.: esse metodo chama outros metodos privados

	String nome    = organization.getName();
	String alias   = organization.getAliasName();
	String domains = organization.getDomain();		
	
	if (organization.getDaylightSaving() != null) {
		if (!organization.getCountry().equals(organization.getDaylightSaving().getCountry())) {
			facesMessages.addFromResourceBundle(Severity.WARN, 
					"organization.message.dstCountryError");
			return;
		}
	}
	
	
	if (isOrganizationExists()) {

		if (nome.equals(nome1)) {
			facesMessages.add(Severity.WARN, "#{messages['organization.sameName']}");				
			return;
		}

		if (alias.equals(alias1)) {
			facesMessages.add(Severity.WARN, "#{messages['organization.sameAlias']}");				
			return;
		}

		if (domains.equals(domains1)) {
			facesMessages.add(Severity.WARN, "#{messages['organization.sameDomain']}");				
			return;
		}		

	} else {
		
		if (entityManager.contains(organization)) {
			boolean isProviderT = false;
			boolean isProviderB = false;					

			boolean isCustomerT = false;
			boolean isCustomerB = false;

			HibernateSessionProxy session = ((HibernateSessionProxy) entityManager.getDelegate());

			//SELECIONADO NA TELA?
			for (OrganizationType selectedOrganizationType : organizationType) { 		   						   				
				if (selectedOrganizationType == OrganizationType.SERVICE_PROVIDER) {
					isProviderT = true;									
				}
				if (selectedOrganizationType == OrganizationType.CUSTOMER) {
					isCustomerT = true;									
				}
			} 					

			//REGISTRADO NO BANCO?
			Criteria criteriaType = session.createCriteria(OrganizationOrganizationType.class);
			criteriaType.add(Restrictions.eq("id.organizationID", this.organization.getId()));	

			List<OrganizationOrganizationType> resultTypeList = criteriaType.list();	   	   		

			for (OrganizationOrganizationType organizationOrganizationType : resultTypeList) {			   	   				   				
				if (organizationOrganizationType.getOrganizationType() == OrganizationType.SERVICE_PROVIDER) {
					isProviderB = true;									
				}	
				if (organizationOrganizationType.getOrganizationType() == OrganizationType.CUSTOMER) {
					isCustomerB = true;									
				}	
			}	

			//ORGANIZATION TYPE - SEM ALTERAÇÃO
			if (((isProviderB && isProviderT) && ((!isCustomerB) && (!isCustomerT)))
					|| (((!isProviderB) && (!isProviderT)) && (isCustomerB && isCustomerT))
					|| ((isProviderB && isProviderT) && (isCustomerB && isCustomerT))) {
				saveDeactivated();
				entityManager.flush();
				facesMessages.add(Severity.INFO, "#{messages['operation.editionSuccess']}");
				return;	
			}	

			if ((isServiceBladeExists() && ((isProviderB && (!isProviderT)) && ((!isCustomerB) && (!isCustomerT)))) 
					|| (isServiceBladeExists() && ((isCustomerB && (!isCustomerT)) && ((!isProviderB) && (!isProviderT))))) {			   			
				facesMessages.add(Severity.INFO, "#{messages['organization.serviceblade']}");
				return;
			}			   	

			//PROVIDER -> SE FOR REMOVIDO, VERIFICA SE HÁ ENDPOINT
			if (isProviderB && (!isProviderT)) {

				if (isServiceBladeExists()) { 
					facesMessages.add(Severity.INFO, "#{messages['organization.serviceblade']}");
					return;			   				
				}

				if (isEndpointOrganizationExists()) { 
					removeEndpointOrganization();	
					removeTypesOrganization();
					entityManager.flush();
					saveOrganizationType();	
					entityManager.flush();
					saveDeactivated(); 
					entityManager.flush();
					facesMessages.add(Severity.INFO, "#{messages['organization.endpoint']}#{messages['operation.editionSuccess']}");
					return;			   				
				} else {
					alterOrganization();
					return;								
				}							  
			} else {			   	   		
				alterOrganization();
				return;				   	   		
			}	

		} else {
			
			entityManager.persist(organization);
			saveOrganizationType();					
			saveOrganizationKeyStore();
			entityManager.flush();					
			facesMessages.add(Severity.INFO, "#{messages['operation.insertionSuccess']}");	
		}		
	}
}

}[/code]

interface:

[code]
public interface OrganizationManagerInterface {

void save();

}[/code]

Obs.: Na pagina o usuario clica no botão Salvar e o metodo é chamado via interface.

Se precisar de mais alguma informação é só pedir.

Obrigada!

Wow!!! Tome cuidado com o tamanho dos seus métodos, métodos muito grandes ñ só são difíceis de entender como difíceis de testar. Acoselho q vc de uma estudada em refactoring q vai te ajudar bastante.
Bom p/ te ajudar aqui é interessante q vc procure por um framework de mock e nesse caso eu aconselho o Mockito. Comece mudando o código desse modo:

// antes estava assim...
@In private EntityManager entityManager;
@In private FacesMessages facesMessages;

// agora vai ficar assim...
@In EntityManager em;
@In FacesMessages fmsgs;
/* O "em" como nome do EntityManager é um padrão sugerido pela Sun no lançamento do JEE5. Nomes curtos p/ objetos como FacesContexts, EntityManagers, etc... pode te ajudar a poupar um pouco de
código sem perder em clareza posto q raramente vc terá mais q um objeto desse tipo em um determinado contexto e os nomes dessas classes já são bastante sugestivas a respeito do seu papel, sem falar q elas fazem parte do framework assim o conhecimento sobre o papel desses objetos é implícito (ao menos deveria ser) */

É preciso fazer isso pois de outro modo vc ñ conseguirá simular a injeção. Para o teste vc pode:

public class ClientManagerTest {
  @Mock FacesMessages fmsgs;

  ClientManager subject;

  @Before
  public void setUp() {
    MockitoAnnotations.initMocks(this);
    subject = new ClientManager();
    subject.fmsgs = fmsgs;
  }

  /* para testar:
     if (organization.getDaylightSaving() != null) {  
            if (!organization.getCountry().equals(organization.getDaylightSaving().getCountry())) {  
                facesMessages.addFromResourceBundle(Severity.WARN,   
                        "organization.message.dstCountryError");  
                return;  
            }  
        } 
   */
  @Test
  public void shouldDisplayAWarn() {
    Country c = new Country();
    subject.getOrganization().setDaylightSaving(new DaylightSaving());
    subject.getOrganization().setCountry(c);
    subject.getDaylightSaving().setCountry(c);

    subject.save();

    verify(fmsgs).addFromResourceBundle(Severity.WARN, "organization.message.dstCountryError");
  }
}

Esse código é só um exemplo e está longe de estar bom. Na prática o seu método save() vai precisar de um monte de testes como este p/ testar todas os possíveis fluxos. Refatora-lo vai ajudar bastante a obter testes mais simples e mais fáceis de manter. Desculpe mas eu ñ consegui visualizar um meio simples de demonstrar o teste de banco nesse código. Mas se vc for faze-lo lembre-se de criar um EntityManager como eu demonstrei no post anterior e mantenha um banco de testes com dados controlados, por exemplo, uma tabela ORGANIZACAO com uns 4 registros q nunca mudam. Assim fica mais fácil de escrever asserções p/ os seus testes e manter esses testes se os dados não mudarem.
Ñ sei se ajudei o bastante, qualquer outra duvida posta ai eu vou tentar ajuda-la. Caso esteja interessada, posso te ajudar a refatorar esse código :)!!

Mais uma vez obrigada!

Irei fazer as alterações q vc indicou.

Quanto ao tamanho dos metodos, umas das tarefas q tenho é justamente melhorar isso.

Veja o erro q esta acontecendo, qto a primeira parte do codigo q vc postou:

[code]@BeforeClass
public static void setUpClass() {
Map<String, Object> override = new HashMap<String, Object>();
override.put(“javax.persistence.transactionType”, “RESOURCE_LOCAL”);
override.put(“javax.persistence.jdbc.driver”, “net.sourceforge.jtds.jdbc.Driver”);
override.put(“javax.persistence.jdbc.url”, “jdbc:jtds:sqlserver://11.111.1.111/ConformedDB;instance=xxxxx”);
override.put(“javax.persistence.jdbc.user”, “xxx”);
override.put(“javax.persistence.jdbc.password”, “xxx”);

	     try {
	    	 em = Persistence.createEntityManagerFactory("ClientManager", override).createEntityManager();  
		} catch (Exception e) {
			e.printStackTrace();
		}
	     
    }  

[/code]

Erro1: Linha 11
javax.persistence.PersistenceException: No Persistence provider for EntityManager named ClientManager: The following providers:
org.hibernate.ejb.HibernatePersistence
oracle.toplink.essentials.PersistenceProvider
oracle.toplink.essentials.ejb.cmp3.EntityManagerFactoryProvider
Returned null to createEntityManagerFactory.

Erro2: The method getTransaction() is undefined for the type ClientManager

@Before  
   public void setUp() {  
	ClientManager em = new ClientManager();  
	cm.em = em;  		   
	em.getTransaction().begin();  
 } 

P/ q esse código funcione é preciso q o arquivo persistence.xml q vc usa no projeto principal esteja acessível tb às classes de teste. Na verdade o JPA vai criar o EntityManager com base nas configurações desse arquivo mesmo, mas vc vai modificar algumas q te interessam.
De uma olhada nessa página q pode te ajudar melhor a criar o seu EntityManager de teste.

[quote=dev.rafael]P/ q esse código funcione é preciso q o arquivo persistence.xml q vc usa no projeto principal esteja acessível tb às classes de teste. Na verdade o JPA vai criar o EntityManager com base nas configurações desse arquivo mesmo, mas vc vai modificar algumas q te interessam.
De uma olhada nessa página q pode te ajudar melhor a criar o seu EntityManager de teste.[/quote]

Olá dev.rafael!

Consegui criar o EntityManager colocando o persiste.xml no projeto como vc indicou, porém ainda esta acontecendo um erro na injeção de dependencia do Hibernate Session.

Na classe ClientManager no metodo save() dá erro na seguinte linha:

HibernateSessionProxy session = ((HibernateSessionProxy) entityManager.getDelegate());

Vc pode dá mais alguma dica?

Muito grata!

Posta ai o código de inicialização do seu EntityManager e o seu persistence.xml p/ eu dar uma olhada. Eu desaconselho q vc obtenha o HibernateSessionProxy se vc está injetando o EntityManager. A menos q estritamente necessário (tipo p/ ter acesso em um ponto específico a uma funcionalidade q o JPA ñ oferece) é melhor q use apenas o JPA ou apenas o Hibernate (se vc estiver chamando getDelegate() com frequência). Esse tipo de chamada pode tornar o seu código menos legível e mais difícil de manter.

Entendi o q vc disse, mais no momento não posso mexer em nada de estrutura, somente realizar os testes mesmos.

Persiste.xml

[code]<?xml version="1.0" encoding="UTF-8"?>

<persistence-unit name="sieManagementPool" transaction-type="JTA" >
	<provider>org.hibernate.ejb.HibernatePersistence</provider>
	<jta-data-source>jdbc/sieManagementPool</jta-data-source>
	<jar-file>sieManagementBase.jar</jar-file>
	<properties>
		<property name="hibernate.show_sql" value="false" />
		<property name="hibernate.format_sql" value="false" />
		<property name="hibernate.dialect" value="org.hibernate.dialect.SQLServerDialect" />
		<property name="hibernate.connection.isolation" value="4096" />
		<property name="hibernate.query.startup_check" value="false" />
		<property name="hibernate.default_batch_fetch_size" value="30" />
		<property name="hibernate.transaction.manager_lookup_class"
			value="org.hibernate.transaction.SunONETransactionManagerLookup" />

	</properties>
</persistence-unit>

<persistence-unit name="managementSIEBase" transaction-type="JTA" >
	<provider>org.hibernate.ejb.HibernatePersistence</provider>
	<jta-data-source>jdbc/OnlineMultichannelPool</jta-data-source>
	<jar-file>SIEBase.jar</jar-file>
	<properties>
		<property name="hibernate.show_sql" value="false" />
		<property name="hibernate.format_sql" value="false" />
		<property name="hibernate.dialect" value="com.diebold.sie.base.hibernate.ext.ExtendedSQLServerDialect" />
		<property name="hibernate.connection.isolation" value="4096" />
		<property name="hibernate.query.startup_check" value="false" />
		<property name="hibernate.default_batch_fetch_size" value="30" />
		<property name="hibernate.transaction.manager_lookup_class"
			value="org.hibernate.transaction.SunONETransactionManagerLookup" />

		<property name="hibernate.cache.use_query_cache" value="true" />
		<property name="hibernate.cache.use_minimal_puts" value="false" />
		<property name="hibernate.cache.use_second_level_cache" value="true" />
		<!-- property name="hibernate.cache.use_structured_entries" value="true" / -->
		
        
	</properties>
</persistence-unit>

[/code]

[code]@BeforeClass
public static void setUpClass() {
Map<String, Object> override = new HashMap<String, Object>();
override.put(“javax.persistence.transactionType”, “RESOURCE_LOCAL”);
override.put(“javax.persistence.jdbc.driver”, “net.sourceforge.jtds.jdbc.Driver”);
override.put(“javax.persistence.jdbc.url”, “jdbc:jtds:sqlserver://11.111.1.111/SIEConformedDB;instance=xxxxx”);
override.put(“javax.persistence.jdbc.user”, “usr”);
override.put(“javax.persistence.jdbc.password”, “senha”);

	     try {
	    	 em = Persistence.createEntityManagerFactory("sieManagementPool", override).createEntityManager();  
		} catch (Exception e) {
			e.printStackTrace();
		}
	     
    }  [/code]

Obs.: Irei “pegar” todas as dicas q vc me passou e conversar aqui para começarmos a fazer melhorias no projeto.

Mais uma vez, Obrigada!

Olha agora eu realmente ñ sei qual é o problema. Eu ñ costumo expor a API do provider quando estou desenvolvendo com JPA pq, geralmente, o JPA já satisfaz as minhas necessidades. Mas assim q eu chegar em casa eu faço um teste e verei se consigo descobrir alguma coisa só posta p/ a versão do hibernate q vc está utilizando.

Mais uma dica, pelo jeito o servidor de banco q vc está utilizando nos testes está em uma máquina remota (ip=11.111.1.111). Testes de integração com banco de dados ficam mais fáceis de vc usar um banco local (de preferência um banco in-memory como o HDBSQL) e um script p/ inicialização dos dados no banco. Assim vc terá total segurança sobre o conteudo do banco. Veja o seguinte código:

/* Esse código é apenas um exemplo. Num programa real vc deve evitar os DAOs. */
public class ItemsDAO {

  /* Vc tb ñ vai testar um método desses. */
  public List<Items> searchAll() {
    return em.createNamedQuery(Items.ALL).getResultList();
  }

}

E o teste:

@Test
public void testSearchAll() {
  List<Items> items = itemsDAO.searchAll();

  assertEquals(5, items.size());
}

Mas e se outra pessoa insere mais um ou remove alguma entidade Item do banco? O seu teste para de funcionar! Então use um banco de testes local com um script p/ te ajudar a inicializa-lo (p/ ficar mais fácil vai criando o script conforme o necessário) pq assim vc sempre saberá quando e quais items estarão na tabela ITEMS.