Argument(s) are different! WHY???

14,770 views
Skip to first unread message

Gavião

unread,
Nov 12, 2009, 3:39:11 PM11/12/09
to mockito
Hi,
I started this week in the unit test world and I choosed mockito to
play with...
Today I write an example to try get things more clear on my head, but
I got a bit confused... :-/
I run and debug my app on eclipse and everything is ok. But my mockito
test fails.
It seams that mockito is not recording the state of my bean after runs
mportLogDao.include....

Could somebody point me what I did wrong on my test or app for mockito
is given me this error:

Argument(s) are different! Wanted:
importLogDao.include(
ImportLogBean [importProcessType=0, status=1, processDate=Thu Nov
12 00:00:00 GMT-03:00 2009]
);
-> at
com.dry.importmanager.ImportManagerTest.testStartImportProcessWhichFailsWithOtherRunningProcess
(ImportManagerTest.java:43)
Actual invocation has different arguments:
importLogDao.include(
ImportLogBean [importProcessType=0, status=9, processDate=Thu Nov
12 00:00:00 GMT-03:00 2009]
);
-> at com.dry.importmanager.ImportManager.createResume
(ImportManager.java:49)


Here is my examples classes and test:

public class ImportManagerTest {

ImportManager importManager;
ImportLogDao importLogDao;
IImportHandler importHandler;

@Before
public void setUp() throws Exception {
importLogDao = Mockito.mock(ImportLogDao.class);
importHandler = Mockito.mock(IImportHandler.class);
importManager = new ImportManager(importLogDao);
}

@Test
public void testStartImportProcessWhichFailsWithOtherRunningProcess
() {

int importType = 0;
Date currentDate = new GregorianCalendar(2009,10,12).getTime();

ImportLogBean initialLog = new ImportLogBean(currentDate,
importType);
initialLog.setStatus(1);

ImportLogBean finalLog = new ImportLogBean(currentDate,
importType);
finalLog.setStatus(5);

given(importLogDao.anyImportRunningOrRunnedToday(importType,
currentDate)).willReturn(false);

importManager.startImportProcess(importType, currentDate);

verify(importLogDao).include(initialLog);

verify(importLogDao).alter(finalLog);

}
}


public class ImportManager {

public ImportManager(ImportLogDao pImportLogDao) {
super();
importLogDao = pImportLogDao;
}
private ImportLogDao importLogDao = new ImportLogDao();

public void startImportProcess(int importType, Date date) {
ImportLogBean importLogBean = null;

try {
importLogBean = createResume(importType, date);
if (isOkToImport(importType, date)) {
// get the right handler
importLogBean = ImportHandlerFactory.singleton().getImportHandler
(importType).processImport(
importLogBean);
// 2 = ok
importLogBean.setStatus(2);
} else {
// 5 = failed - is there a running process
importLogBean.setStatus(9);
}
} catch (Exception e) {
// 9 = failed - exception
if (importLogBean != null)
importLogBean.setStatus(9);
} finally {
if (importLogBean != null)
finalizeResume(importLogBean);
}
}

private boolean isOkToImport(int importType, Date date) {
return importLogDao.anyImportRunningOrRunnedToday(importType, date);
}

private ImportLogBean createResume(int importType, Date date) {
ImportLogBean importLogBean = new ImportLogBean(date,
importType);
// 1 = running
importLogBean.setStatus(1);
importLogDao.include(importLogBean);
return importLogBean;
}

private void finalizeResume(ImportLogBean importLogBean) {
importLogDao.alter(importLogBean);
}
}

public class ImportLogBean {

public ImportLogBean(Date pProcessDate, int pImportProcessType) {
super();
processDate = pProcessDate;
importProcessType = pImportProcessType;
status = 0;
log = "";
}
private Date processDate;
private int importProcessType;
private String log;
private int status;
...
...
}

Thanks for any help

Cristiano

Graham Allan

unread,
Nov 12, 2009, 4:00:28 PM11/12/09
to moc...@googlegroups.com
After a brief glance, I can't say exactly what the problem is, but I do have a
suggestion:

The actual importLogBean has a status of 9, one of the places where this is
set is in the following code:

catch (Exception e) {
// 9 = failed - exception
if (importLogBean != null)
importLogBean.setStatus(9);
}

Perhaps an exception is happening in the test because there is setup involved
which is done in your app, but not in the test. For example, perhaps
ImportHandlerFactory.singleton() throws an exception which is then being
caught and masked. My suggestion is to debug the test and see if an exception
you didn't expect is thrown, or even put in an e.printStackTrace(). It may
highlight a difference in setup between the test and the running app.

HTH,
Graham

Cristiano Gavião

unread,
Nov 12, 2009, 4:26:40 PM11/12/09
to moc...@googlegroups.com
Hi Grahan,

In reallity, the part of code you point is not being called because in my test I do:

given(importLogDao.anyImportRunningOrRunnedToday(importType, currentDate)).willReturn(false);
           
So it do not enter in this part of  code because of this:

            if (isOkToImport(importType, date))


but anyway thanks for reply.

Cristiano


Graham Allan escreveu:

Malte Finsterwalder

unread,
Nov 12, 2009, 4:41:16 PM11/12/09
to moc...@googlegroups.com
Hello Cristiano,

I think the problem is, that you are altering the captured object
after handing it to the mock.
So when you call the verify, the object is by then altered to status==9.
An EasyMock-like mock would work better for this case. EasyMock
verifies the calls, when they happen, not afterwards.
I don't know off the top of my head, whether this can be done with
mockito, though.

Greetings,
Malte

Graham Allan

unread,
Nov 12, 2009, 5:00:29 PM11/12/09
to moc...@googlegroups.com
> In reallity, the part of code you point is not being called because in my
> test I do:
>
> given(importLogDao.anyImportRunningOrRunnedToday(importType,
> currentDate)).willReturn(false);
> So it do not enter in this part of  code because of this:
>
>             if (isOkToImport(importType, date))
>
>
> but anyway thanks for reply.
>
> Cristiano


The idea would have still held true for the line:
importLogBean = createResume(importType, date);
... but now I realise, not for the include() method, only the alter() method.


Nope, really can't see from the example how the status can change between
these two lines:
importLogBean.setStatus(1);
importLogDao.include(importLogBean);

Sorry I couldn't help, if you figure it out let me know what the problem was.

~ Graham

Graham Allan

unread,
Nov 12, 2009, 5:05:33 PM11/12/09
to moc...@googlegroups.com
> I think the problem is, that you are altering the captured object
> after handing it to the mock.

That's a good point, if the call to alter() sets the status to 9 it may be
causing the problem. If you change the line:
verify(importLogDao).include(initialLog);
to:
verify(importLogDao).include(new ImportLogBeen(currentDate, importType);

Does it work? (This is assuming a correct equals() method.

~ Graham

Graham Allan

unread,
Nov 12, 2009, 5:12:54 PM11/12/09
to mockito
No, forget that.I'm talking nonsense, I shall now go get some sleep :-)

Cristiano Gavião

unread,
Nov 12, 2009, 5:20:10 PM11/12/09
to moc...@googlegroups.com
Hi Graham,

I think what Malte Finsterwalder said in last email is true:
I think the problem is, that you are altering the captured object
after handing it to the mock.
So when you call the verify, the object is by then altered to status==9.
In that part of test :       verify(importLogDao).include(initialLog);

Mocito  is using  the last version of the ImportLogBean object to compare with my writted initialLog, not the version that was used when method importLogDao.include(importLogBean);
was called.

But now, I don't know if it is limitation of mockito or a kinf of issue..

regards

Cristiano


Graham Allan escreveu:

szczepiq

unread,
Nov 13, 2009, 2:49:22 AM11/13/09
to moc...@googlegroups.com
Hi guys,

As you figured, the problem is when argument is mutated between
passing to mock objects. I'd say it's a limitation of spying. It is
fixable but not very easily... There's an issue for it:
http://code.google.com/p/mockito/issues/detail?id=126

Hope that helps,
Szczepan Faber

Cristiano Gavião

unread,
Nov 13, 2009, 8:00:46 AM11/13/09
to moc...@googlegroups.com
Hi, Szczepan !  

My business classes, as ImportManager, are singleton (managed by our DI framework). So
I can't have class atributes on it. I just have methods with java beans (mutable) being passed as arguments to them.

So, if I cannot use verify() to check if a internal method of my manager class was called, ex: verify(importLogDao).include(initialLog);
Today, is there a way to use Mockito to test those beans state inside my methods?

If yes, could you give some ideas how can I do it?

thanks in advance

cheers


 ps:
 :-)  man, it is really dificult for a brazilian just to imagine how could I speach your name !!!  :-D 

szczepiq escreveu:

szczepiq

unread,
Nov 13, 2009, 9:22:45 AM11/13/09
to moc...@googlegroups.com
> :-) man, it is really dificult for a brazilian just to imagine how could I
> speach your name !!! :-D

c'mon man, it's just a couple of zeds more =)

> Today, is there a way to use Mockito to test those beans state inside my
> methods?

I would probably make ImportLogBean immutable (which probably should
be immutable anyway - It smells funny that you create log, send an it
to importLogDao and then you change the state of log....). Or at least
create a different log if I need different status.

Cheers,
Szczepan Faber

Cristiano Gavião

unread,
Nov 13, 2009, 11:02:29 AM11/13/09
to moc...@googlegroups.com
szczepiq escreveu:
 :-)  man, it is really dificult for a brazilian just to imagine how could I
speach your name !!!  :-D
    
c'mon man, it's just a couple of zeds more =)

  
:-)
Today, is there a way to use Mockito to test those beans state inside my
methods?
    
I would probably make ImportLogBean immutable (which probably should
be immutable  anyway - It smells funny that you create log, send an it
to importLogDao and then you change the state of log....). Or at least
create a different log if I need different status.

  
In reallity,  I've modified and tried to translate those classes names and methods, just to send them for the mockito list, because here we use portuguese...
About the business entity ImportLogBean, I've used the english LOG because I couldn't find another proper name in time...

Let me explain better... its not just a log...  It's a object that was initialized on the request of a user or another systems that need integration with our internal departamental system (by file or by webservice or by direct access to system databases)...  So it has informations about the requested/sending system, parameters about the import itself and how it was processed.

I need to save it when it arrives just to know that is already one request of the it's kind running or requested.. (there is a rule that we can't start more than one import process by day).

And this object will concentrate all information when process is running too... including any message of break of rules, processed row numbers, etc... So it can't be immutable...

But there is some important point that I would like you help me clarify.
Our context is:
- In our framework (its no ejb), all classes that concentrate our business rules (we could name Business Manager or Business Object or BO) are singleton, and inside them there are only public void methods that receive Entity Objects as parameters...

We have already learned that we can use mockito to mock/stub dao's and webservices calls. But how could we use mockito to make tests on those Business Rules methods and Entity Objects parameters?

 Thanks in advance and best regards

Cristiano

szczepiq

unread,
Nov 13, 2009, 2:14:17 PM11/13/09
to moc...@googlegroups.com
Hmmm... in that case I implementing your own Answer (the code is
below). It is a bit of a workaround. Expect-run-verify mocking
framework (like EasyMock) might be a better fit here (although, I
would use Mockito anyway :-). Thanks for submitting this case because
it got me thinking on ideas how to improve Mockito.


@Test
public void shouldIncludeInitialLog() {
//given
int importType = 0;
Date currentDate = new GregorianCalendar(2009, 10, 12).getTime();

ImportLogBean initialLog = new ImportLogBean(currentDate, importType);
initialLog.setStatus(1);

given(importLogDao.anyImportRunningOrRunnedToday(importType,
currentDate)).willReturn(false);
willAnswer(byCheckingLogEquals(initialLog)).given(importLogDao).include(any(ImportLogBean.class));

//when
importManager.startImportProcess(importType, currentDate);

//then
verify(importLogDao).include(any(ImportLogBean.class));
}

@Test
public void shouldAlterFinalLog() {
//given
int importType = 0;
Date currentDate = new GregorianCalendar(2009, 10, 12).getTime();

ImportLogBean finalLog = new ImportLogBean(currentDate, importType);
finalLog.setStatus(9);

given(importLogDao.anyImportRunningOrRunnedToday(importType,
currentDate)).willReturn(false);
willAnswer(byCheckingLogEquals(finalLog)).given(importLogDao).alter(any(ImportLogBean.class));

//when
importManager.startImportProcess(importType, currentDate);

//then
verify(importLogDao).alter(any(ImportLogBean.class));
}

private Answer byCheckingLogEquals(final ImportLogBean status) {
return new Answer() {
public Object answer(InvocationOnMock invocation) throws Throwable {
ImportLogBean bean = (ImportLogBean)
invocation.getArguments()[0];
assertEquals(status, bean);
return null;
}
};
}

Cheers,
Szczepan Faber

Cristiano Gavião

unread,
Nov 16, 2009, 12:48:30 PM11/16/09
to moc...@googlegroups.com
Hi Szczepan,

I restart my study on unit test today....

And I would like to thank you for your priceless help!!!

I've got glad that I could help you too. and we will still bet on mockito !! :-)

best regards

Cristiano

szczepiq escreveu:
Reply all
Reply to author
Forward
0 new messages