Make reusable mocks

Hi guys,

I finally pulled up the courage to dive into unit testing and I seem to be doing ok. Starting to use mockbox to mock my dependencies.

One thing I notice is that there is no real documentation on how to use the mocks folder in the test folder. I figure it must be to create reusable mocks, which would be awesome as I like to code DRY.

After some testing I came up with putting the following in my mocks folder:

MockFactory.cfc

component {

property name=“mockBox” type=“coldbox.system.testing.MockBox”;

MockFactory function init(){
mockBox = new coldbox.system.testing.MockBox();
return this;
}

any function getWireBox(){
var model = mockBox.createEmptyMock(“coldbox.system.ioc.Injector”);
model
.$(“getInstance”).$args(“Order@beans”).$results(getOrder())
.$(“getInstance”).$args(“OrderDetail@beans”).$results(getOrderDetail());
return model;
}

any function getOrder(){
var model = mockBox.createEmptyMock(“model.beans.Order”);
model
.$(“getProfile”).$results(getProfile());
return model;
}

any function getOrderDetail(){
var model = mockBox.createEmptyMock(“model.beans.OrderDetail”);
return model;
}

any function getProfile(){
var model = mockBox.createEmptyMock(“model.beans.Profile”);
model
.$(“getContact”).$results(“Luis”,“Craig”,“Tom”);
return model;
}

any function getProfileService(){
var model = mockBox.createEmptyMock(“model.services.ProfileService”);
return model;
}

any function getDocumentService(){
var model = mockBox.createEmptyMock(“model.services.DocumentService”);
return model;
}

}

I also added the model and the mocks folder to my mappings in application.cfc in the test folder:

this.mappings["/model’] = “path to model”;
this.mappings["/mocks’] = “path to mocks”;

So I don’t need to worry about using full paths to components in my model or mocks. Mainly handy when different developers have a different code base (web root).

Now when I Unit test my OrderService I can simply add the following in my setup:

Extract from OrderServiceTest.cfc

component extends=“coldbox.system.testing.BaseModelTest”{

void function setup(){
//Call the super setup method to setup the app.
super.setup();

// Your own setup here if needed
mockFactory = createObject(“component”,“mocks.MockFactory”).init();
model
.$property(“wireBox”,“variables”,mockFactory.getWireBox())
.$property(“profileService”,“variables”,mockFactory.getProfileService())
.$property(“documentService”,“variables”,mockFactory.getDocumentService())
.$property(“orderForm”,“variables”,mockFactory.getOrderForm())
.$property(“voucherSelectForm”,“variables”,mockFactory.getVoucherSelectForm())
.$property(“cgcDAO”,“variables”,mockFactory.getCorporateGiftCardDAO());

}

function testGetOrder() {
assertEquals(“Luis”, model.getOrder(0).getProfile().getContact());
assertEquals(“Craig”, model.getOrder(0).getProfile().getContact());
assertEquals(“Tom”, model.getOrder(0).getProfile().getContact());
}
}

Does that make sense or is there a better way to reuse Mocks that are shared among different unit tests?

Input is welcome :slight_smile:

+1 on your question Tom, this is something I’d like to know as well.

Robert

One note…

in order for the asserts to be valid i should have typed:

function testGetOrder() {
var order = model.getOrder(0);
assertEquals(“Luis”, order.getProfile().getContact());
assertEquals(“Craig”, order.getProfile().getContact());
assertEquals(“Tom”, order.getProfile().getContact());
}

otherwise it just recreates the order and the call to getContact will allways return Luis (hehehe, the alpha and the omega?)

Cheers,
Tom

Tom, you got it. The idea behind the mocks folder is for reusable mocks, either like you have done or by creating real CFC mocks that need a physical representation rather than a dynamic one which MockBox provides. I would say MockBox provides 95% capabilities, but there are cases where you need reusable mocks. So what you did is EXACTLY a great way to reuse mocks.

Mocks also some time take time to prepare, so I use it like you did to prepare hard to mock objects or objects that have lots of dependencies, that way, you do not repeat yourself in all of the unit tests.

Can I use your example for the official ColdBox docs?

Luis F. Majano
CEO
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

Hi Luis,

Thanks for the reply and glad to be on the right track :slight_smile:

You sure can, I would suggest you remove the lines:

.$property(“orderForm”,“variables”,mockFactory.getOrderForm())
.$property(“voucherSelectForm”,“variables”,mockFactory.getVoucherSelectForm())
.$property(“cgcDAO”,“variables”,mockFactory.getCorporateGiftCardDAO())

As I didn’t declare them in the MockFactory and it might be confusing.

Cheers,
Tom

Also, the simplyfied getOrder function in my OrderService looks something like this like this:

any function getOrder(numeric orderID = 0){
var order = wirebox.getInstance(“Order@beans”);
if (orderID > 0){
/* to do
cgcDAO.populateOrder(orderID, order);*/
}
return order;
}

Which is why I needed to mock wirebox.

Cheers,
Tom