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!
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.