[Coldbox-3.6.0] Mocking transients

I know - it sounds like making fun of homeless people. :S

I have this injection metadata at the top of a model:

and later, in a function I request a dependency and start using it:
variables.injector.getInstance(“MyTransientDependency”).doSomeStuff()

In writing my unit test I want to mock the MyTransientDependency object, but I’m not sure how to go about it, whereas it’s provided by wirebox.

To give a more real-world example, let’s say wirebox has getInstance() called on it multiple times within the same function, with different names passed in each time. So mocking the return of the getInstance function would be a pain, and (if something like $results() were used) a maintenance nightmare if the order the dependencies were internally requested in changed without changing the behavior of the function in a way that would require updating the unit test. That approach just feels icky anyway.

I’m guessing there’s a straightforward way to hijack the variables.injector reference and give my component a wirebox instance (or something like it) that will return the mocks I specify in my unit test rather than the actual objects mapped in my app’s binder (Wirebox.cfc)… and do it based on the names passed in to the getInstance() function within the code I’m testing.

Where that gets a bit tricky is if within a single function you want to test you get multiple instances of the same named object on which you need to mock the same function to return different results (like the function you test needs 2 ‘person’ objects and initializes one to be a person of type A and the other to be a person of type B and the person component is expected to return differently structured data from a particular function you need to mock. In such a case you may not be able to get away from using something like $results(), and maybe that’s what it’s there for.

But my main issue is what is the ‘right way’ to mock transients?

Ah… nevermind. Just needed to read the docs a bit closer:

http://wiki.coldbox.org/wiki/MockBox.cfm#.24args() outlines the use of $args() to mock the result of the combination of a function and a specific argument (or set of args).

For an example of use, see the getWireBox function in this example code:https://groups.google.com/forum/#!msg/coldbox/FoXPjEf798g/rVKhN-8VY34J

We mock wirebox and then use $args() to mock the getInstance function when different values are passed in.

OK. After writing a unit test and getting it working using this method I’m wondering if there isn’t a better way to do it. I’ll post a code sample for reference so you can see exactly what I’m working with. What I want to know is whether there’s a way to avoid some of the setup involved. I’ve highlighted the lines I’m referring to below. When the app runs it uses the mappings in the binder and instantiates the actual objects. What I instinctively want to do is rather than doing the first highlighted line using createEmptyMock followed by the second line where I am doing some hand-holding of the mock injector, to instead maybe do createMock and have a real wirebox be returned where a test binder that it’s given knows I want the mockDAO instead of whatever I had mapped for the app in my Wirebox.cfc binder. The tricky part would then be to still be able to do any further setup on the mockDAO in the test setup for things like the querysim and so on.

Note that the mockBox instance is provided for in the base test component extended by the test cfc that this unit test is a part of. /internal/cfc is where we’re keeping our model cfc’s.

<cfset local.financial = mockBox.createMock(“o3.internal.cfc.financial”) />
<cfset local.mockWireBox = mockBox.createEmptyMock(“coldbox.system.ioc.Injector”) />
<cfset local.mockUser = mockBox.createMock(“o2.internal.cfc.User”) />

<cfset local.mockDAO = mockBox.createMock(“o3.internal.cfc.FinancialDAO”) />
<cfset local.refundRequestQuery = mockBox.querysim(“refund_card_auth,status,participant,amount,order_no
42 | Requested | 12345678 | 100.00 | 12121212”) />

<cfset local.financial.$property(“injector”, “variables”, local.mockWireBox) />

<cfset local.mockDAO.$(“getRefundRequest”, local.refundRequestQuery) />
<cfset local.mockWireBox.$(“getInstance”).$args(“FinancialDAO”).$results(local.mockDAO) />
<cfset local.financial.$(“finalizeBankcardTxn”) />
<cfset local.financial.$(“insertPayment”) />

<cfset local.financial.finalizeRefund(1,2,“fakeServerTransactionId”,“fakeCardType”,“fakeReconcileId”) />

And here’s the line from the finalizeRefund function that we’re trying to test that is the impetus for the highlighted lines above:

<cfset local.refundRequest = variables.injector.getInstance(“FinancialDAO”).getRefundRequest(arguments.refundRequestId) />

Is there a simpler way to do the test setup? Does anyone have any best practices examples? Is there a way to make unit testing with IOC in coldbox a little easier? I had considered maybe doing something like this in my tests:

<cfset local.setupConfig = {
mocks = [“o3.internal.cfc.financial”, “coldbox.system.ioc.Injector”, “o2.internal.cfc.User”, “o3.internal.cfc.FinancialDAO”]
} />

instead of

<cfset local.financial = mockBox.createMock(“o3.internal.cfc.financial”) />
<cfset local.mockWireBox = mockBox.createEmptyMock(“coldbox.system.ioc.Injector”) />
<cfset local.mockUser = mockBox.createMock(“o2.internal.cfc.User”) />

<cfset local.mockDAO = mockBox.createMock(“o3.internal.cfc.FinancialDAO”) />

and then having a base test case cfc that all my test cfc’s extend that reads this and does all the mock creation for me. I suppose that doesn’t address the fact that the second highlighted line still feels like we’re manually doing wirebox’s job for it. I really would like to keep my test setup to more simple mocking of return values rather than doing something that feels like mimicking some of the wirebox internal implementation in my test code.

Thoughts?