Mockbox: Wrapping / Dynamically extending an object

Hi group,

I’ve been doing some reading up on Unit Testing databases / DAO through MXUnit. The suggested approach seems to be using a Test Adapter Pattern like so:-
http://wiki.mxunit.org/display/default/DAO+Test+Adapter+Pattern

Reading it I’m not getting a good vibe though. If I’m needing to run tests against all my Services and then by extending I’m going to need an adaptor for every Service, even if the transaction code is the same.

So I’m wondering if there is a better way of doing this where I can create AN adapter that I can use against any Service.

Dynamically extending seems to be a fail. ColdFusion just flips out on me and needs a restart.

What I’m thinking is I need some kind of Unit Testing Proxy (AOP possibly) that stands in front of the Service methods and allows me to wrap transactions around my Unit Testing code.

Another suggestion approach to me was…

MockBox can overwrite methods but I’m wonder if there is a way I could add in some kind of Proxy method that would allow me to test against it without overwriting the method I’m testing.

Not sure what’s possible but I thought I’d ask the question :slight_smile:

Cheers,
James

Hi James,

This is going to turn into an interesting conversation, I can tell already. :smiley:

When I was talking with people about database interaction in unit / integration tests a while back people were talking about a couple of different approaches.

People then were talking about ways of handling transactions in the setUp() and tearDown() methods which prep the transactions and then roll it back once the tests are complete, so nothing is actually committed to the DB.

Are you using ORM, native or transfer based? Do you have pure cfquery tags which manipulate data?

Rawlins

Hey Rob,

Under the bonnet is Transfer :slight_smile:

That’s a possibility with the setup / teardown approach but I’ll need to have a rethink on how I lay my tests out, I’ve got other non-database tests in there so really no point in them getting mixed up with that stuff.

Only “grumble” I’d have about is approach is I’m potentially duplicating transaction code in all these Test Suite setups. With the more OO approach I could call a proxied version of the Service and run things through there. How you run custom unit tests half way through though I’m not sure… I’m maybe trying to over science ( ?!? )

Yeah that’s a very fair point. Something AOP / Interceptor based would be a cleaner implementation I suppose.

If you’re using transfer you probably have some decent options. I’ve not done much work with transactions within transfer but I think you can do some clever stuff.

You can access the transaction through transfer as an object, and I believe, even apply AOP advice around transactions, may be a good place to start looking? That way, CB, MXUnit don’t know anything, the transaction management and advice are all handled by Transfer, which would make sense.

http://docs.transfer-orm.com/wiki/Transactions_and_Transfer.cfm

Robert

If you are using a Di framework that supports aop, like Coldspring, then it should be easy enough to do this, I would have thought.

Just have a different config for your unit tests, that includes your core application configuration, and use aop to intercept the methods you want.

Although the tricky bit would be specifying return values. Maybe an annotation based approach?

This has some interesting potential as an idea.

Mark

Great stuff, cheers for the input guys.

I’m using Lightwire at the moment, which I don’t think supports AOP, but I’ll have a head scratch about it and see what I come up with :slight_smile:

Cheers,
James

I am with mark that this is something that can be aop nature. However it is specific to testing so could it be directed where we can create a base transaction safe annotation on our tests and we can wrap them in transactions for you?

How about

Function testMyService() transactional{
}

Then the base test case can read it and proxy it with a transaction?

I already do this for the base orm services so I could reuse it to proxy the method for a simple transaction since we are most likely not going to get funky with aop and nested aspects and such.

Now does this make sense?

Luis Majano
President
Ortus Solutions, Corp
Toll free phone/fax: 1-888-557-8057
Mobile: 909-248-3408
www.ortussolutions.com
www.coldbox.org

Thanks Luis, yeah that sounds make sense and would be very cool I think.

Just to give an update, and please correct me if you feel I’ve taken a wrong step in doing this, I had a thought that it’s very likely we’d want transaction locking on the Service DB methods anyway, evening without the testing.

So rather than managing all this through my tests I’ve setup the following method in my Base Service which runs after init() :-

// Enable Transfer Transaction locking on all methods using Transfer variables.instance.transferTransaction.advise(this,'^(save|delete)');

I thought that if anything were to go wrong if even outside the testing then really I’d want to rollback on any commits.

Does that sound sensible enough or is there something I’m maybe missing on the Testing side by doing this?

Cheers again for all the input
James

To tell you the truth, I mostly do what you just pointed out which is transaction my services or data services anyways.

I guess needs further investigation because for testing, you most likely don’t want to commit right?

Luis F. Majano
President
Ortus Solutions, Corp
www.ortussolutions.com

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

Social: twitter.com/lmajano facebook.com/lmajano

“I guess needs further investigation because for testing, you most likely don’t want to commit right?”

Absolutely.

At the moment I’m saving, testing and then deleting. I guess if something were to go wrong between the save & delete then I’ve got a problem on my hands.

I said above already but in terms of doing it with the AoP approach I wasn’t sure how you could say something like :-

Open Generic Transaction in AoP …

< Run my specific testing >

Close Generic Transaction in AoP …

Cheers again,
James