Unit Test CFC Prefix / Suffix

10 views
Skip to first unread message

Jeff Chastain

unread,
May 1, 2008, 11:03:24 AM5/1/08
to MXUnit

I am working on a new project using MXUnit and again ran into a problem where the directory test suite was not picking up my unit test CFCs.  After digging for a while again, I remembered that the unit test objects had to be prefixed or suffixed with ‘test’.  I keep all of my unit test CFCs in a separate folder structure from my application code which mirrors the folder structure of my model.  Therefore, I know all of the CFCs there are unit tests and I want to keep the file names the same for easy comparison … thus they don’t have the ‘test’ prefix or suffix.

 

So, rather than hack the core, I created a new DirectoryTestSuite.cfc which extended the existing one within the framework and over-rode 3 methods …. run(), getTests(), and accept().   Nothing was changed in the run() or getTests() method with the exception of adding a new argument called ‘componentPrefix’ which is optional and defaults to ‘test’.  This argument value is then passed from run() to getTests(), and then on to accept().   In the accept() method, I changed line 106 to read:

 

                <cfif len(arguments.componentPrefix) AND NOT reFindNoCase( "^#arguments.componentPrefix#", testName ) AND NOT reFindNoCase( "#arguments.componentPrefix#$", testName ) >

 

By making this change, the code first checks to see if the componentPrefix value is set and then if so, uses the componentPrefix value for testing instead of the hard coded ‘test’ string as is currently the case.

 

By making the arguments optional and default to ‘test’, this is fully backward compatible and just provides the extra flexibility in how you wish to name the unit test CFCs.  I have attached my DirectoryTestSuite.cfc to this message (assuming it makes it through Google) if anybody wants it or if the powers at be would consider integrating the change into the core.

 

Thanks

-- Jeff

DirectoryTestSuite.cfc

Marc Esher

unread,
May 1, 2008, 1:58:13 PM5/1/08
to mxu...@googlegroups.com
Thanks Jeff.

Bill and I have had some discussions about this in the past and were
kind of holding off until we got some guidance on the "need" for this.

Here's something to think about: we can change the framework in the
manner you describe. However, this will not change the behavior of the
plugin because currently, just as in DirectoryTestSuite, those
patterns are hard coded.

So, what I'm thinking is that if we provide the flexibility to change
the patterns, then maybe it'd be smart to do it in such a way that
either a) it was changed in the framework and the plugin would be able
to ask the framework for the pattern to use or b) there'd be a new
preference in the plugin for the patterns to look at, and if we went
that route, then ideally you'd do the same thing in both places. I.e.
it'd be a string like "*test.cfc|test*.cfc" that you'd use in both
the framework (maybe in the mxunit-config.xml file) and the new plugin
preference.

Now, just writing this out, I'm kind of preferable to A myself: having
the framework know the pattern to use (which would be
*test.cfc|test*.cfc by default) and having that pattern be exposed,
such that RemoteFacade can provide a getTestPattern() method that the
plugin can call.

So, with that in mind, can you rethink the design to achieve this kind
of flexibility? Again, the key idea here will be to set that pattern
in a single place and have every call to every directorytestsuite (i
have many different ones in my code... not just one Uber suite) and
also the plugin know about this pattern.

Thanks for your contribution. We really appreciate it!

Marc

Jeff Chastain

unread,
May 1, 2008, 2:05:20 PM5/1/08
to mxu...@googlegroups.com
Marc,

That makes perfect sense. I have not been using the plugin lately, but I
can see exactly how this change would not be picked up there. I don't know
much about the plugin or eclipse plugins in general, but ideally there would
be a configuration file that contained settings like this that could be used
by the web front end as well as the eclipse plugin.

Again, this may not be possible with the plugin, but I would wonder if the
plugin would simply be another "view" of the application like the web front
end would be. This way (not sure where or why), I could potentially extend
the application by putting a web services front end on it for example.

I can't offer much in terms of the eclipse plugin, but I would be glad to
help with an investigation on how to pull out some of these hard coded
configuration settings out of the core - either online or off. Just let me
know.

Thanks
-- Jeff


Thanks Jeff.

Marc

> tests and I want to keep the file names the same for easy comparison .


thus
> they don't have the 'test' prefix or suffix.
>
>
>
> So, rather than hack the core, I created a new DirectoryTestSuite.cfc
which

> extended the existing one within the framework and over-rode 3 methods ..

Marc Esher

unread,
May 1, 2008, 2:26:19 PM5/1/08
to mxu...@googlegroups.com
Jeff,
You wouldn't have to worry about any of that junk. All I'd need is a
way to say "hey, mxunit, what pattern should be used for determining
whether a cfc is a test?" currently I just have a hard-coded regular
expression. so ideally i'd just have to add a new method into
RemoteFacade.cfc, and the plugin would call that.

Now, probably the brain-dead easiest way to achieve all of this is
to add a new element into the mxunit-config.xml file. that way, the
pattern is set in one place. the downside to this is that it'd be a
framework-wide, so you couldn't necessarily modify it at the project
level, and that may or may not be a problem. So then maybe you'd be
looking into not just a single element for the test pattern, but then
potential overrides, like:

<test-patterns>
<default-pattern>*test.cfc,test*.cfc</default-pattern>
<project-pattern project="/my/app">*</project-pattern>
</test-patterns>

this is just thinking out loud, but hopefully you get the idea.

I guess my point here is that I wouldn't want to just pass in a
pattern in a suite.cfm file because then that pattern is inaccessible
to the plugin. what we need is a way to set it on a single place so
that you wouldn't need to pass it in the directory test suite at
all... it'd just know because the framework knows its patterns.

is that any clearer?

thanks.

marc

Jeff Chastain

unread,
May 1, 2008, 2:39:39 PM5/1/08
to mxu...@googlegroups.com
Marc,

That makes perfect sense and was the direction I was headed. If this was
implemented right, you could have a per-application configuration file like
many of the other frameworks (M2, MG, Coldspring) such that you could have
these settings change on a per-application basis rather than just having one
config file for framework. So, in addition to having all of my unit test
CFCs and an index file that runs the directory test suite, there could be an
xml configuration file whose path is passed to the test runner. The same
could work for the eclipse-plugin in that there could be a setting to point
to the root folder where it would find the standardized xml file.

Christopher Bradford

unread,
May 1, 2008, 3:12:40 PM5/1/08
to mxu...@googlegroups.com
I'm testing a Reactor object retrieved from ColdSpring. Because of the
way Reactor works, what I end up getting is a database-specific object
that inherits from a large inheritance tree. I needed to test a private
method that is actually defined on a superclass of the object I get back
from ColdSpring. I discovered that MXUnit's makePublic functionality
doesn't currently support private functions in superclasses. I've
attached a patch for your consideration that adds support for making
private functions in superclasses public. The patch is for the
PublicProxyMakerTest and the PublicProxyMaker.

Christopher Bradford

MXUnit_MakePublicFromSuperclass.patch

Marc Esher

unread,
May 1, 2008, 4:25:49 PM5/1/08
to mxu...@googlegroups.com
hey Chris. Good catch. I fixed this back on April 9th but we haven't
put out a release since then. Were you using a release of the
framework or were you getting straight from SVN? The reason I ask is
that If you were using the one from subversion I'm curious why the fix
wasn't working.

Thanks.

Marc

> Index: framework/PublicProxyMaker.cfc
> ===================================================================
> --- framework/PublicProxyMaker.cfc (revision 1169)
> +++ framework/PublicProxyMaker.cfc (working copy)
> @@ -3,10 +3,10 @@
> <cfset cu = createObject("component","ComponentUtils")>
> <cfset lineSep = cu.getLineSeparator()>
> <cfset dirSep = cu.getSeparator()>
> -
> -
> +
> +
> <cffunction name="makePublic" access="public" hint="creates a public method proxy for the indicated private method for the passed-in object" returntype="WEB-INF.cftags.component">
> -
> +
> <cfargument name="ObjectUnderTest" required="true" type="WEB-INF.cftags.component" hint="an instance of the object with a private method to be proxied">
> <cfargument name="privateMethodName" required="true" type="string" hint="name of the private method to be proxied">
> <cfargument name="proxyMethodName" required="false" type="string" default="" hint="name of the proxy method name to be used; if not passed, defaults to the name of the private method prefixed with an underscore">
> @@ -21,81 +21,86 @@
> <cfset var proxy = "">
> <cfset var s_args = "">
> <cfset var componentReturnTag = "return">
> -
> +
> <cfif StructIsEmpty(methodStruct)>
> <cfthrow message="Attempting to make public proxy for private method: method named #privateMethodName# did not exist in object of type #md.name#">
> - </cfif>
> -
> + </cfif>
> +
> <cfif NOT len(trim(proxyMethodName))>
> <cfset proxyMethodName = "_#privateMethodName#">
> - </cfif>
> -
> + </cfif>
> +
> <cfset cfcnotation = proxyMethodName & "_#createUUID()#">
> <cfset file = cfcnotation & ".cfc">
> <cfif StructKeyExists(methodStruct,"returntype") and methodStruct.returntype eq "void">
> <cfset componentReturnTag = "set">
> </cfif>
> -
> +
> <!--- for now, it seems safest to just do this every time, even if it's technically overkill --->
> <cfif not DirectoryExists(dir)>
> <cfdirectory action="create" directory="#dir#">
> - </cfif>
> -
> + </cfif>
> +
> <!--- generate a CFC that contains a public method. this method will call the private method we want to call --->
> <cfset s_args = constructArgumentsTags(methodStruct)>
> -
> +
> <cfoutput>
> - <cfsavecontent variable="output">
> + <cfsavecontent variable="output">
> <%cfcomponent% extends="#md.name#">
> <%cffunction name="#proxyMethodName#" access="public"%>
> #s_args.tags#
> <%cf#componentReturnTag# #methodStruct.name#(#s_args.args#)%>
> <%/cffunction%>
> -
> +
> <%cffunction name="#privateMethodName#" access="public"%>
> #s_args.tags#
> <%cf#componentReturnTag# super.#privateMethodName#(#s_args.args#)%>
> <%/cffunction%>
> -
> +
> <%/cfcomponent%>
> </cfsavecontent>
> </cfoutput>
> <cfset cfcode = replace(output,"%","","all")>
> <cffile action="write" file="#dir##file#" output="#cfcode#">
> -
> +
> <!--- now, create an instance of that generated object --->
> <cfset proxy = createObject("component","mxunit.framework.generated.#cfcnotation#")>
> -
> +
> <!--- inject that function's proxy method into the object passed in; now we can call this new method, which will call the private method --->
> <cfset ObjectUnderTest[proxyMethodName] = proxy[proxyMethodName]>
> -
> +
> <!--- cleanup; i doubt this will be enough so we'll need some way of periodically cleaning out that directory I suspect --->
> - <cffile action="delete" file="#dir##file#">
> + <cffile action="delete" file="#dir##file#">
> <cfreturn proxy>
> </cffunction>
> -
> +
> <cffunction name="findMethodStruct" returntype="struct" access="private" hint="returns the metadata struct for a given method name">
> <cfargument name="metadata" required="true" type="struct" hint="a structure returned from getMetadata">
> <cfargument name="methodName" required="true" type="string" hint="the method to search for">
> -
> +
> <cfset var methodStruct = StructNew()>
> <cfset var i = 1>
> -
> -
> -
> -
> - <cfloop from="1" to="#ArrayLen(metadata.functions)#" index="i">
> - <cfif metadata.functions[i].name eq arguments.methodName>
> - <cfreturn metadata.functions[i]>
> +
> + <cfif structKeyExists(metadata, "functions")>
> + <cfloop from="1" to="#ArrayLen(metadata.functions)#" index="i">
> + <cfif metadata.functions[i].name eq arguments.methodName>
> + <cfreturn metadata.functions[i]>
> + </cfif>
> + </cfloop>
> + <!--- If we get here, we haven't found the function; check superclasses --->
> + <cfif structKeyExists(metadata, "extends")>
> + <cfreturn findMethodStruct(metadata.extends, methodName) />
> </cfif>
> - </cfloop>
> - <!--- if we get to here without finding anything, try to climb the class hierarchy --->
> - <cfset methodStruct = findMethodStruct(metadata.extends,methodName)>
> -
> + <cfelse> <!--- Check superclasses, if any --->
> + <cfif structKeyExists(metadata, "extends")>
> + <cfreturn findMethodStruct(metadata.extends, methodName) />
> + </cfif>
> + </cfif>
> +
> <cfreturn methodStruct>
> -
> +
> </cffunction>
> -
> +
> <cffunction name="constructArgumentsTags" returntype="struct" access="private" hint="creates the cfargument tags, the method call to the private method, and the return statement for the component">
> <cfargument name="privateMethodStruct" type="struct" hint="the structure of metadata for the private method under consideration">
> <cfset var strArgs = "">
> @@ -106,8 +111,8 @@
> <cfset var p = 1>
> <cfset var pCount = ArrayLen(a_params)>
> <cfset var s_return = StructNew()>
> -
> -
> +
> +
> <cfloop from="1" to="#pCount#" index="p">
> <cfparam name="a_params[p].default" default="">
> <cfparam name="a_params[p].required" default="false">
> @@ -121,8 +126,8 @@
> <cfreturn s_return>
> <!--- <cfreturn strArgTags & lineSep & "<cfreturn #privateMethodStruct.name#(#strArgs#)>"> --->
> </cffunction>
> -
> -
>
>
> +
> +
> </cfcomponent>
> \ No newline at end of file
> Index: tests/framework/PublicProxyMakerTest.cfc
> ===================================================================
> --- tests/framework/PublicProxyMakerTest.cfc (revision 1169)
> +++ tests/framework/PublicProxyMakerTest.cfc (working copy)
> @@ -2,34 +2,34 @@
>
> <cfset maker = "">
> <cfset objectWithPrivateMethod = "">
> -
> +
> <cffunction name="setup">
> <cfset maker = createObject("component","mxunit.framework.PublicProxyMaker")>
> <cfset objectWithPrivateMethod = createObject("component","PublicProxyMakerTest")>
> </cffunction>
> -
> +
> <cffunction name="testSanity">
> -
> +
> <cftry>
> - <cfset str = objectWithPrivateMethod.aPrivateMethod()>
> + <cfset str = objectWithPrivateMethod.aPrivateMethod()>
> <cfset fail("should not get to here; should've thrown an error trying to run a private method")>
> <cfcatch type="mxunit.exception.AssertionFailedError">
> <cfrethrow>
> </cfcatch>
> <cfcatch type="any"></cfcatch>
> </cftry>
> -
> +
> </cffunction>
> -
> -
> +
> +
> <!--- here's how to use the object returned by the proxy maker to invoke
> the method with the exact method name under test; under the hood, this creates
> a new component that extends the object under test and adds a public version
> - of the private method. it simply calls super.Whatever(args....)
> -
> - The primary disadvantage is that you lose anything you've done to the object in setUp()
> + of the private method. it simply calls super.Whatever(args....)
> +
> + The primary disadvantage is that you lose anything you've done to the object in setUp()
> or any other initialization prior to running the function since you're now working with a new object
> -
> +
> see testMakePublicObjectWithInit for how to overcome this disadvantage
>
> --->
> @@ -38,14 +38,14 @@
> <cfset ret = proxy.aPrivateMethod("one","two","++")>
> <cfset assertEquals("one++two",ret)>
> </cffunction>
> -
> +
> <cffunction name="testMakePublicObjectNoArg">
> <cfset proxy = maker.makePublic(objectWithPrivateMethod,"aNoArgPrivateMethod")>
> <!--- if it doesn't fail, all is OK --->
> <cfset ret = proxy.aNoArgPrivateMethod()>
> <cfset assertEquals("boo",ret)>
> </cffunction>
> -
> +
> <cffunction name="testMakePublicObjectWithInit">
> <!--- so that you don't lose any of the initialization you'd do in setup, you can perform
> that initialization inside the object itself AFTER making the public proxy --->
> @@ -53,47 +53,47 @@
> <!--- just an example:
> <cfset proxy.init(blah,blah)>
> <cfset proxy.setSomethingOrOther(foo)>
> -
> +
> --->
> <cfset ret = proxy.aPrivateMethod("one","two","()")>
> </cffunction>
> -
> +
> <cffunction name="testMakePublicObjectVoid">
> - <cfset proxy = maker.makePublic(objectWithPrivateMethod,"aPrivateVoid")>
> + <cfset proxy = maker.makePublic(objectWithPrivateMethod,"aPrivateVoid")>
> <cfset ret = proxy.aPrivateVoid()>
> <cfset assertEquals(5,proxy.x)>
> </cffunction>
> -
> -
> +
> +
> <!---
> here's how to use the proxy method created and injected into the existing object under test.
> -
> +
> the primary benefit of this version over the "proxy object" version is that you retain any
> - object modification done prior to invoking the method.
> -
> + object modification done prior to invoking the method.
> +
> --->
> -
> -
> +
> +
> <cffunction name="testMakePublicNamedArgs">
> - <cfset maker.makePublic(objectWithPrivateMethod,"aPrivateMethod","_aPrivateMethod")>
> + <cfset maker.makePublic(objectWithPrivateMethod,"aPrivateMethod","_aPrivateMethod")>
> <cfset result = objectWithPrivateMethod._aPrivateMethod(arg1="one",arg2="two")>
> - <cfset assertEquals("one_two",result)>
> -
> + <cfset assertEquals("one_two",result)>
> +
> <cfset result = objectWithPrivateMethod._aPrivateMethod(arg1="one",arg2="two",sep="~~")>
> - <cfset assertEquals("one~~two",result)>
> + <cfset assertEquals("one~~two",result)>
> </cffunction>
> -
> +
> <cffunction name="testMakePublicNonNamedArgs">
> <cfset maker.makePublic(objectWithPrivateMethod,"aPrivateMethod","_aPrivateMethod")>
> <cfset result = objectWithPrivateMethod._aPrivateMethod("one","two","+")>
> <cfset assertEquals("one+two",result)>
> <cfset result = objectWithPrivateMethod._aPrivateMethod("one","two","_")>
> <cfset assertEquals("one_two",result)>
> -
> +
> <cfset result = objectWithPrivateMethod._aPrivateMethod("one","two")>
> <cfset assertEquals("one_two",result)>
> </cffunction>
> -
> +
> <cffunction name="testMakePublicNonExistentMethod">
> <cftry>
> <cfset maker.makePublic(objectWithPrivateMethod,"aPrivateMethodThatDoesNotExist","_aPrivateMethod")>
> @@ -103,89 +103,89 @@
> </cfcatch>
> <cfcatch></cfcatch>
> </cftry>
> - </cffunction>
> -
> + </cffunction>
> +
> <cffunction name="testMakePublicNoThirdArg">
> <!--- we're simply testing here that the name of the resultant public method to use exists and doesn't fail --->
> - <cfset maker.makePublic(objectWithPrivateMethod,"aPrivateMethod")>
> + <cfset maker.makePublic(objectWithPrivateMethod,"aPrivateMethod")>
> <cfset result = objectWithPrivateMethod._aPrivateMethod(arg1="one",arg2="two")>
> - <cfset assertEquals("one_two",result)>
> + <cfset assertEquals("one_two",result)>
> </cffunction>
> -
> +
> <cffunction name="testMakePublicNoArgMethod">
> <cfset maker.makePublic(objectWithPrivateMethod,"aNoArgPrivateMethod","_aNoArgPrivateMethod")>
> <!--- simply test that it doesn't error --->
> <cfset ret = objectWithPrivateMethod._aNoArgPrivateMethod()>
> <cfset assertEquals("boo",ret)>
> </cffunction>
> -
> +
> <cffunction name="testMakePublicVoid">
> - <cfset maker.makePublic(objectWithPrivateMethod,"aPrivateVoid")>
> + <cfset maker.makePublic(objectWithPrivateMethod,"aPrivateVoid")>
> <cfset ret = objectWithPrivateMethod._aPrivateVoid()>
> <cfset assertEquals(5,objectWithPrivateMethod.x)>
> </cffunction>
> -
> +
> <cffunction name="testMakePublicNoReturnType">
> <cfset maker.makePublic(objectWithPrivateMethod,"aPrivateMethodNoRT")>
> <cfset ret = objectWithPrivateMethod._aPrivateMethodNoRT>
> <cfset assertTrue(len(ret) GT 0)>
> </cffunction>
> -
> +
> <cffunction name="testMakePublicArray">
> <cfset maker.makePublic(objectWithPrivateMethod,"aPrivateMethodReturnArray")>
> <cfset ret = objectWithPrivateMethod._aPrivateMethodReturnArray()>
> <cfset assertTrue(isArray(ret),"returned value should be an array")>
> </cffunction>
> -
> +
> <cffunction name="testMakePublicArray2">
> <cfset maker.makePublic(objectWithPrivateMethod,"aPrivateMethodReturnArray2")>
> <cfset ret = objectWithPrivateMethod._aPrivateMethodReturnArray2()>
> <cfset assertTrue(isArray(ret),"returned value should be an array")>
> </cffunction>
> -
> +
> <cffunction name="testMakePublicSuperClassMethod">
> - <!--- simply don't error! --->
> - <!--- <cfset debug(getMetadata(this))> --->
> <cfset maker.makePublic(objectWithPrivateMethod,"createResult")>
> + <cfset result = objectWithPrivateMethod._createResult()>
> + <cfset assertTrue(findNoCase("TestResult", getMetadata(result).NAME))>
> </cffunction>
> -
>
> -
> +
> +
> <!--- these are the private methods we're going to make public since we're using an instance of this component as the object under test --->
> -
> +
> <cffunction name="aPrivateMethod" access="private" returntype="string">
> <cfargument name="arg1" type="string" required="true">
> <cfargument name="arg2" type="string" required="true">
> <cfargument name="sep" type="string" required="false" default="_">
> <cfreturn arg1 & sep & arg2>
> - </cffunction>
> -
> + </cffunction>
> +
> <cffunction name="aNoArgPrivateMethod" access="private" returntype="string">
> <cfreturn "boo">
> - </cffunction>
> -
> + </cffunction>
> +
> <cffunction name="aPrivateMethodNoRT">
> <cfset var purpose = "no return type specified">
> <cfreturn purpose>
> </cffunction>
> -
> +
> <cffunction name="aPrivateMethodReturnArray">
> <cfreturn ArrayNew(1)>
> </cffunction>
> -
> +
> <cffunction name="aPrivateMethodReturnArray2" returntype="array">
> <cfreturn ArrayNew(1)>
> </cffunction>
>
> <!--- this will run as constructor code --->
> - <cfset this.x = 1>
> + <cfset this.x = 1>
> <cffunction name="aPrivateVoid" access="private" returntype="void">
> <cfset this.x = 5>
> </cffunction>
> -
> -
> -
> -
>
>
> +
> +
> +
> +
> </cfcomponent>
> \ No newline at end of file
>
>

Christopher Bradford

unread,
May 1, 2008, 6:11:43 PM5/1/08
to mxu...@googlegroups.com
Marc,

I am using the current RC release. I see a partial fix in SVN, but it
still breaks in my situation, because the FUNCTIONS struct element
doesn't exist in the metadata for an empty CFC that extends another (a
common scenario with Reactor). It will throw an error before reaching
your code that climbs the class hierarchy. In addition, it appears that
your code will break if it climbs the hierarchy all the way up to the
root component, which doesn't have an EXTENDS struct element in its
metadata, without finding the method name (it will try to pass
metadata.extends to the recursive call).

Chris

Marc Esher wrote:
> hey Chris. Good catch. I fixed this back on April 9th but we haven't
> put out a release since then. Were you using a release of the
> framework or were you getting straight from SVN? The reason I ask is
> that If you were using the one from subversion I'm curious why the fix
> wasn't working.
>
> Thanks.
>
> Marc

--
Christopher Bradford

Marc Esher

unread,
May 1, 2008, 6:20:02 PM5/1/08
to mxu...@googlegroups.com
Cool! I"ll get the fix in and put it out there tonight.

Thanks a lot for the patches, Chris.

Marc

Marc Esher

unread,
May 2, 2008, 7:42:55 AM5/2/08
to mxu...@googlegroups.com
chris, Thanks again for the patch. I was curious to see what you were
talking about with the empty subclass, so I wrote a new test for that
condition, reverted to my old "fix", and got the error you described.
Your patch fixed it.

best,

marc

Christopher Bradford

unread,
May 2, 2008, 9:40:38 AM5/2/08
to mxu...@googlegroups.com
Glad to help out. I should have added a test for that condition in my
patch. :-)

Chris

Marc Esher wrote:
> chris, Thanks again for the patch. I was curious to see what you were
> talking about with the empty subclass, so I wrote a new test for that
> condition, reverted to my old "fix", and got the error you described.
> Your patch fixed it.
>
> best,
>
> marc

--
Christopher Bradford

Marc Esher

unread,
May 2, 2008, 10:07:49 AM5/2/08
to mxu...@googlegroups.com
Yes, you should have, and we'll suffer no such lapses in the future.

;-)

marc

Alan Livie

unread,
May 2, 2008, 10:28:47 AM5/2/08
to mxu...@googlegroups.com
This must be this new 'TestLast' movement :-)
________________________________________
From: mxu...@googlegroups.com [mxu...@googlegroups.com] On Behalf Of Marc Esher [marc....@gmail.com]
Sent: 02 May 2008 15:07
To: mxu...@googlegroups.com
Subject: [mxunit:374] Re: MakePublic for superclass methods

Marc Esher

unread,
May 2, 2008, 11:14:49 AM5/2/08
to mxu...@googlegroups.com
Well... it is all the rage in the pate and caviar set...
Reply all
Reply to author
Forward
0 new messages