Small confusion when passing argument Collection

What I'm trying to do is create a mock method and then pass it an
argumentCollection
which is the normal way I pass arguments to functions. However if I do
this with a
mocked method, it returns an error:

"The value returned from the testme function is not of type query."

here is a brief overview of the problem in question:

//set up the mock method, note the arguments
oSearchDAO.$(method="testme").$args(1,1).$results(myquery);

//call the mock function and save results, note the arguments passed
in by argumentCollection
stujay.kermit = 1;
stujay.gonzo = 1;
stuReturn.rstResults = oSearchDAO.testme(argumentCollection = stujay);

You can see that argument collection doesn't match the args, even
though arguments.toString() (which is what the mockbox file seems to
use) evaluates the two to the same thing.

Anyway, it caused me some confusion. Just thought I'd mention it.

The problem is that you are passing the argumentCollection to the call, which in turn uses NAMED parameters. However, when you are mocking it, you are using positional parameters, therefore they are NOT the same.

.$args(1,1)
is not the same as
.$args(kermit=1,gonzo=1)

They will produce different signature strings.

Luis F. Majano
President
Ortus Solutions, Corp

ColdBox Platform: http://www.coldbox.org
Linked In: http://www.linkedin.com/pub/3/731/483
Blog: http://www.luismajano.com
IECFUG Manager: http://www.iecfug.com

Hi Luis,

Thanks for the reply.

If i may ask another question what method is prefered, the following
method is even more unstable. If you hit refresh it works roughly one
in five:

<cfscript>

  myQuery = queryNew('empty');
  oMockBox = createObject("component",
"mockbox.system.testing.MockBox").init();
  oSearchDAO = oMockBox.createStub();
  myNewQuery = queryNew('empty');

  myQuery = queryNew('name,age,height');
  queryAddRow(myQuery);
  querySetCell(myQuery,"name","kermit");
  querySetCell(myQuery,"age",18);
  querySetCell(myQuery,"height","4ft");

  osearchdao.$(method="searchdestinationbyid").$args(
            intlocationid = -1,
            strlongstring = '',
            strshortstring = 'berlin',
            strlongparentstring = '',
            strshortparentstring = '',
            isactiveonly = 1,
            intnumnights = 2).$results(myquery);

  mynewquery = osearchdao.searchdestinationbyid(
            intlocationid = -1,
            strlongstring = '',
            strshortstring = 'berlin',
            strlongparentstring = '',
            strshortparentstring = '',
            isactiveonly = 1,
            intnumnights = 2);

</cfscript>

<cfdump var="#myNewQuery#">

G'day
I've been defactoring jaydeveloper's code a bit to get as simple an
example as possible. I've got this far:

<cfscript>
  oMockBox = createObject("component",
"mockbox.system.testing.MockBox").init();

  oMocked = oMockBox.createStub();

  qMockResult = queryNew("usr_id,usr_firstname,usr_lastname");

  queryAddRow(qMockResult);
  querySetCell(qMockResult, "usr_id", 1);
  querySetCell(qMockResult, "usr_firstname", "Adam");
  querySetCell(qMockResult, "usr_lastname", "Cameron");

  oMocked.$(method="mockedMethod").$args(
    usrid = 1,
    isactiveonly= "Adam",
    usrlastname = "Cameron"
  ).$results(qMockResult);

  qResult = oMocked.mockedMethod(
    usrid = 1,
    isactiveonly= "Adam",
    usrlastname = "Cameron"
  );
</cfscript>

<cfdump var="#qResult#">

(I've been slowly defactoring argument names to see if I can isolate
what the problem is here, hence the odd argument name "isactiveonly").

This sample code runs fine about 50% of the time, the other 50% of the
time the mockedMethod() call returns null.

If I change "isactiveonly" to "usrFirstName" (or anything else I've
tried, actually), then the code seems to work 100% of the time. The
arguments in both the mockMethod definition and the method call are
identical: I'm copying and pasting them with each change.

Weird.

I've seen similar instances of this thing happening on three machines
(all with disparate installs) now.

Does anyone have any ideas here?

Has no-one else come across this?

If we weren't seeing it across multiple machines, all set up by
different people, all on different tests I'd just assume it's
something we're doing and not the software. But I just can't see how
this is not a (pretty serious) bug..?

Cheers for any thoughts.

Ahh... In case anyone else comes by this...

I forgot CF will capitalize all struct keys so this worked for me to
mock a method that is called with arguemntCollection:

...
<cfset LOCAL.mockTaxCodeDAO.$("getTaxCodeById").
$args(INTTAXCODEID="1234").$results(LOCAL.mockTaxCode2)>
...

yep, its a CF issue.
Luis F. Majano
President
Ortus Solutions, Corp

ColdBox Platform: http://www.coldbox.org
Linked In: http://www.linkedin.com/pub/3/731/483
Blog: http://www.luismajano.com
IECFUG Manager: http://www.iecfug.com

How is it a CF issue?

I've looked into this some more, and it seems to me that MockBox is
making some assumptions about the toString() method of
coldfusion.runtime.ArgumentCollection which are not necessarily true.
This code demonstrates:

<!--- TesthashArgs.cfc --->
<cfcomponent>

  <cffunction name="f" returntype="struct">
    <cfset var stReturn = structNew()>

    <!--- what mockbox does --->
    <cfset stReturn.toString = arguments.toString()><!--- this doesn't
always return same results from call to call --->
    <cfset stReturn.hash = hash(stReturn.toString)><!--- therefore the
hash is not necessarily the same. However mockbox RELIES on this
always being the same --->

    <!--- seeing if native CF handles it better: no --->
    <cfwddx action="cfml2wddx" input="#arguments#"
output="stReturn.wddx"><!--- this also doesn't always return same
results from call to call --->
    <cfset stReturn.wddx = htmlEditFormat(stReturn.wddx)><!--- just
escaping it so we can see actual value in DUMP --->

    <cfreturn stReturn>
  </cffunction>

</cfcomponent>

<!--- testhashArgs.cfm --->
<cfset o = createObject("component", "TestHashArgs")>

<cfset dteArgDate = now()>

<cfset st1 = o.f(
  strArg1 = "foo",
  dateArg = dteArgDate
)>

<cfset st2 = o.f(
  strArg1 = "foo",
  dateArg = dteArgDate
)>

<cfif st1.hash eq st2.hash>
  OK
<cfelse>
  <cfdump var="#st1#" label="st1">
  <cfdump var="#st2#" label="st2">
</cfif>

About 50% of the time, toString() resolves the order of the keys of
the argumentCollection in a different order, so the resultant string
is different, so the hash is different. The %age of success varies
depending on the names of the args, which is quite strange.

However the result is entirely predictable - indeed I would have
expected it to fail more often - because the keys of a struct are
intrinsically not ordered, so no assumptions ought to be made about
their ordering. Which is where MockBox goes wrong: it relies on there
being intrinsic ordering in the serialised arguments which simply
isn't there.

I'm afraid this is a bug in MockBox, not a "CF issue", because MockBox
is making this invalid assumption :frowning:

Just in case anyone's interested in dealing with this bug, Bill from
the MXUnit project has fixed the similar issue with MXUnit's mocking
implementation.

Details here: http://tinyurl.com/2vj4mso (this is the relevant thread
on the MXUnit mailing list).

Cheers.

Thanks will add this to the list but you are welcome to submit a patch too

Adam, I got inspired after reading billy's post on using a treeMap to
order the values, so I just updated a fix to this in SVN and targeted
for MockBox 1.3 release. But it should be all good on SVN now.
Please try again.

Cheers mate, that did indeed fix our issue.