[RESOLVIDO] definindo o comportamento esperado com o EasyMock

opa!

Estou tentando aprender a usar o EasyMock para poder criar testes de unitários criando os objetos mocks para as dependências, e estou tendo dificuldade para entender uma situação, veja:

No meu código que vai ser testado eu tenho duas chamadas a objetos que foram criados mocks e apenas com eles pelo menos o método de teste passa, ,mas tenho ainda, dentro desse método a criação de um objeto DTO que criaria uma mensagem, deixando a parte que cria e define os campos do DTO

public void enviar(final EMail eMail, final Enviador enviador)
			throws AutenticacaoException_Exception,
			ValidacaoException_Exception {
		final Configuracao configuracao = configuracaoModel.get();

		final MensagemDTO mensagemDTO = new MensagemDTO();
		mensagemDTO.setContaEnvio(configuracao.getIdConta());
		mensagemDTO.setDataHoraEnvio(null);
		mensagemDTO.setTexto(eMail.getConteudoMensagem());
		mensagemDTO.setTitulo(eMail.getTitulo());

		enviador.agendarMensagem(MarketingUtil.getAutenticacao(configuracao),
				mensagemDTO);
}

Classe que testa a parte acima:

[code]public class MailerMockTest extends TestCase {

private Enviador enviadorMock;
private ConfiguracaoModel configuracaoModel;

public MailerMockTest() {
	super();
}

private Configuracao getConfiguracao() {
	Configuracao configuracao = new Configuracao();
	configuracao.setSenhaMkt("sss");
	configuracao.setUsuarioMkt(1l);
	configuracao.setUrlWebServiceMkt("http://localhost:8080/mkt");
	configuracao.setIdConta(1);
	configuracao.setListaDiscussao("Fiel");
	return configuracao;
}

private EMail getDomain() {
	EMail email = new EMail();
	email.setId(1);
	email.setPara("wes@terra.com.br");
	email.setTitulo("Titulo");

	return email;
}

@Override
protected void setUp() throws Exception {
	super.setUp();
	enviadorMock = EasyMock.createMock(Enviador.class);
	configuracaoModel = EasyMock.createMock(ConfiguracaoModel.class);
}

public void testCriaEnviaMail() throws AutenticacaoException_Exception,
		ValidacaoException_Exception, MalformedURLException {

	EasyMock.expect(configuracaoModel.get()).andReturn(getConfiguracao());
	final MensagemDTO mensagemDTO = new MensagemDTO();
	mensagemDTO.setContaEnvio(getConfiguracao().getIdConta());
	mensagemDTO.setDataHoraEnvio(null);
	mensagemDTO.setTexto(getDomain().getConteudoMensagem());
	mensagemDTO.setTitulo(getDomain().getTitulo());
	EasyMock.expect(enviadorMock.agendarMensagem(
			new AutenticacaoEmpresaDTO(), mensagemDTO));

	EasyMock.replay(enviadorMock, configuracaoModel);
	MailerService mailer = new MailerService();
	mailer.setConfiguracaoModel(configuracaoModel);
	mailer.enviar(getDomain(), enviadorMock);
	EasyMock.verify(enviadorMock, configuracaoModel);

}

}[/code]

Reparem que ela não entende parte precede a chamada a agendarMensagem como um comportamento esperado (mesmo eu achando que defini corretamente, o que nao deve ser verdade), alguém sabe como definir o comportamento para criar objetos dentro de um método?

java.lang.IllegalStateException: missing behavior definition for the preceding method call agendarMensagem(br.com.ultramax.mkt.ws.AutenticacaoEmpresaDTO@1833955, br.com.ultramax.mkt.ws.MensagemDTO@291aff)
	at org.easymock.internal.MocksControl.replay(MocksControl.java:174)
	at org.easymock.EasyMock.replay(EasyMock.java:1970)
	at br.com.ultramax.gestaoescolar.core.mail.test.unit.model.MailerMockTest.testCriaEnviaMail(MailerMockTest.java:72)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Unknown Source)
	at junit.framework.TestCase.runTest(TestCase.java:164)
	at junit.framework.TestCase.runBare(TestCase.java:130)
	at junit.framework.TestResult$1.protect(TestResult.java:106)
	at junit.framework.TestResult.runProtected(TestResult.java:124)
	at junit.framework.TestResult.run(TestResult.java:109)
	at junit.framework.TestCase.run(TestCase.java:120)
	at org.eclipse.jdt.internal.junit.runner.junit3.JUnit3TestReference.run(JUnit3TestReference.java:130)
	at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

Experimenta implementar o equals e o hashcode nas suas classes DTOs

Vou tentar tb…

mas achei uma explicação em um blog que diz que um método que tenha retorno como eh o caso do meu segundo, retorna um tipo com o resultado da tentativa de agendamento, deve ser definido com andReturn… pelo menos com um pequeno teste que tinha feito deu certo, vou ver com esse agora…

[quote]
Explanation
The issue is that you’ve only half-told EasyMock what you expect to happen. Because the checkSomething() method has a return type other than void, when need to explicitly tell EasyMock what to expect as the return value. That is done by using the andReturn() method. [/quote]

vlw

O que você disse também era necessário no meu caso, mas para o meu azar esses DTOs sao classes geradas através do JAX, não sei se tem um jeito dele criar com equals implementado quando usa o wsimport, vou dar uma pesquisada, mas eu fiz parte do que o blog do cara falava e parte do que você disse e deu certo.

bom, lendo a documentação do EasyMock eu vi que para falar que um argumento na chamado de um método durante um teste seja considerado igual ao usado no código a ser verificado sem precisar o equals e hashCode do objeto em questão, deve-se implementar uma interface chamada IArgumentMatcher e dentro da classe definir a lógica de igualdade.
Depois disso é necessário chamar o método EasyMock.reportMatcher e passar os argumentos que você vai usar durante o teste.
E na parte que verifica o comportamento deve-se usar os Métodos para comparação, no meu caso era o EasyMock.same(objeto.metodoMockiado())

O Problema que ele dá uma mensagem de erro agora que eu entendi que quando uso essa técnica, devo usar esses metodos matcher em todos os argumentos chamados, eu imagino ter colocado em todos, porém o erro (colocado abaixo) persiste, alguém tem alguma idéia?
Outra coisa, o lugar que lança o erro que espera um matcher no argumento, nao tem argumentos, é apenas uma chamada a um método.

java.lang.IllegalStateException: 0 matchers expected, 1 recorded. This exception usually occurs when matchers are mixed with raw values when recording a method: foo(5, eq(6)); // wrong You need to use no matcher at all or a matcher for every single param: foo(eq(5), eq(6)); // right foo(5, 6); // also right at org.easymock.internal.ExpectedInvocation.createMissingMatchers(ExpectedInvocation.java:48) at org.easymock.internal.ExpectedInvocation.<init>(ExpectedInvocation.java:41) at org.easymock.internal.RecordState.invoke(RecordState.java:79) at org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:41) at org.easymock.internal.ObjectMethodsFilter.invoke(ObjectMethodsFilter.java:73) at org.easymock.internal.ClassProxyFactory$MockMethodInterceptor.intercept(ClassProxyFactory.java:92) at br.com.ultramax.gestaoescolar.core.model.ConfiguracaoModel$$EnhancerByCGLIB$$8d488252.get(<generated>) at br.com.ultramax.gestaoescolar.core.mail.test.unit.model.MailerMockTest.testCriaEnviaMail(MailerMockTest.java:73) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at junit.framework.TestCase.runTest(TestCase.java:164) at junit.framework.TestCase.runBare(TestCase.java:130) at junit.framework.TestResult$1.protect(TestResult.java:106) at junit.framework.TestResult.runProtected(TestResult.java:124) at junit.framework.TestResult.run(TestResult.java:109) at junit.framework.TestCase.run(TestCase.java:120) at org.eclipse.jdt.internal.junit.runner.junit3.JUnit3TestReference.run(JUnit3TestReference.java:130) at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

Classe de teste alterada:

[code]public class MailerMockTest extends TestCase {

private Enviador enviadorMock;
private ConfiguracaoModel configuracaoModel;

public MailerMockTest() {
	super();
}

private Configuracao getConfiguracao() {
	Configuracao configuracao = new Configuracao();
	configuracao.setSenhaMkt("sss");
	configuracao.setUsuarioMkt(1l);
	configuracao.setUrlWebServiceMkt("http://localhost:8080/mkt");
	configuracao.setIdConta(1);
	configuracao.setListaDiscussao("Fiel");
	return configuracao;
}

private EMail getDomain() {
	EMail email = new EMail();
	email.setId(1);
	email.setPara("wes@terra.com.br");
	email.setTitulo("Titulo");

	return email;
}

@Override
protected void setUp() throws Exception {
	super.setUp();
	enviadorMock = EasyMock.createMock(Enviador.class);
	configuracaoModel = EasyMock.createMock(ConfiguracaoModel.class);
	EasyMock.reportMatcher(new AutenticacaoEmpresaDTOMatcher()); //Adicionei o Matcher da classe para o EasyMock aqui
}

private ResultadoEnvioDTO getResultadoEnvioDTO() {
	ResultadoEnvioDTO resultadoEnvioDTO = new ResultadoEnvioDTO();
	resultadoEnvioDTO.setMensagem("mensagem");
	resultadoEnvioDTO.setNumeroProtocolo(1l);
	return resultadoEnvioDTO;
}

public void testCriaEnviaMail() throws AutenticacaoException_Exception,
		ValidacaoException_Exception, MalformedURLException {

	EasyMock.expect(configuracaoModel.get()).andReturn(
			getConfiguracao()); //Eu editei essa linha, soh agora reparei que esse metodo nao tem argumentos, e não entendi pq ele fala que precisa de matcher aqui
	final MensagemDTO mensagemDTO = new MensagemDTO();
	mensagemDTO.setContaEnvio(getConfiguracao().getIdConta());
	mensagemDTO.setDataHoraEnvio(null);
	mensagemDTO.setTexto(getDomain().getConteudoMensagem());
	mensagemDTO.setTitulo(getDomain().getTitulo());

	EasyMock.expect(
			enviadorMock.consultaContasEnvio(EasyMock.same(MarketingUtil
					.getAutenticacao(getConfiguracao())))).andReturn(
			new ArrayList<ContaEnvioDTO>());//Uso metodo matcher no argumento aqui também
	EasyMock.replay(enviadorMock, configuracaoModel);
	MailerService mailer = new MailerService();
	mailer.setConfiguracaoModel(configuracaoModel);
	mailer.setEnviador(enviadorMock);
	mailer.enviar(getDomain());
	EasyMock.verify(enviadorMock, configuracaoModel);
}

    //Classe matcher para definir a igualdade, nesse caso, vai pelo cnpj
class AutenticacaoEmpresaDTOMatcher implements IArgumentMatcher {

	@Override
	public void appendTo(StringBuffer arg0) {
		// nada

	}

	@Override
	public boolean matches(Object arg0) {
		final AutenticacaoEmpresaDTO autenticacaoEmpresaDTO = (AutenticacaoEmpresaDTO) arg0;
		return autenticacaoEmpresaDTO.getCpfCnpj() == 1l;
	}

}

}[/code]

Bom eu não entendi o que aconteceu. Mas quando se faz

mesmo as chamadas a métodos que não tinham argumentos apresentava erros, pedindo que fosse colocado um Matcher.
Para resolver eu coloquei esse registro após essa chamada

EasyMock.expect(configuracaoModel.get()).andReturn(getConfiguracao()); EasyMock.reportMatcher(new AutenticacaoEmpresaDTOMatcher());

Outra mudança foi retirar a chamada ao método EasyMock.same que acreditava ser necessário na comparação

Antes:

EasyMock.expect(enviadorMock.consultaContasEnvio(EasyMock.same(getAutenticador()))).andReturn( new ArrayList<ContaEnvioDTO>());

Depois:

EasyMock.expect(enviadorMock.consultaContasEnvio(getAutenticador())) .andReturn(new ArrayList<ContaEnvioDTO>());

Com essas mudanças consegui fazer o código passar e posso agora criar os testes que simulam a conexão com webservice, porém foi um caso que deu certo, mas não sei como, por achar que tinha entendido a documentação da primeira maneira.