4

I have a cfc that relies heavily on a Java object (created via JavaLoader) for a lot of its core functionality that I'd like to write some tests for and I'm not sure what the best way to do this is. Here is an example of a method I'd like to write a test for with instance.note being a java object.

<cffunction name="getNotes" returntype="Array" access="public" output="false" hint="I return a list of a users notebooks" >
    <cfargument name="maxCount" type="numeric" required="false" default="9999" hint="The maximum number of notes to get" />
    <cfscript>
        if(arguments.maxCount)
            return instance.note.listNotes(maxCount);
        else
            return instance.note.listNotes();
    </cfscript>
</cffunction>

One thing I thought of doing is creating a stub CFC that has the same method names and similar return values and then mocking that stub and injecting it?

James A Mohler
  • 11,060
  • 15
  • 46
  • 72
bittersweetryan
  • 3,383
  • 5
  • 28
  • 42
  • Seems to me what you're really doing is testing the underlying Java object. Why not write a jUnit test instead? – orangepips Jul 14 '11 at 12:30
  • The java object does have a junit test already. I just want to test the cfc in isolation to make sure that the methods all do what I want outside of the java object. The code is just one method of many in the object. – bittersweetryan Jul 14 '11 at 17:12

3 Answers3

3

Can't you just write meaningful assertions on the result, i.e. on the array of notes? Looking at that code, the only things I'd test are a) when you pass a maxCount, does your resultant array honor that size? b) without maxCount, is the list of notes the length that you'd expect? Because that's all your code does. I'd test your code, not the code of the underlying java object.

marc esher
  • 4,871
  • 3
  • 36
  • 51
  • I can do that. My concern was that I'd be blurring the lines of unit test vs functional test because I'm relying on the return value of the java object to make my assertion. Maybe I'm just over-thinking this. – bittersweetryan Jul 14 '11 at 17:14
2

When we needed to unit test CF functions that relied upon Java objects (which we did a LOT of), we used Mockito to mock the Java objects.

So, hoping this code snippet makes sense, its been almost a year since I've done this:

<cfcomponent displayname="TestWhatever" extends="mxunit.framework.TestCase" output="false">

    <cffunction name="setUp" access="public" returntype="void">
      <cfscript>
        // named it mk for keeping it short
        variables.mk = createObject("java","org.mockito.Mockito");

        //Create the mock object
        variables.mockNote = mk.mock(createObject("java","com.company.whatever.note").getClass());

        // Mock Data
        fullList = {whatever listNotes() returns}
        partialList3 = {whatever listNotes(3) returns}

        //some common mocking
        mk.when(variables.mockNote.listNotes()).thenReturn(fullList);
        mk.when(variables.mockNote.listNotes(mk.eq(3))).thenReturn(partialList3);
        mk.when(variables.rootOrgObj.guid()).thenReturn("root");

        // Assign the mock object to where your CFC expects it.
        instance.note = variables.mockNote
      </cfscript>
    </cffunction>

</cfcomponent>

Having said that, if your sample function is real, there's really no point in unit testing it. Its simply not doing anything but being a proxy to the java object. There is no significant logic in there to test.

Since you have a default on the cfargument, it will always exist (again, I think so, its been a year since I've done CF), so your guard statement isn't even required - the first code path will always be called, with the passed maxCount if specified, or with 9999 if not.

Edward M Smith
  • 10,627
  • 2
  • 46
  • 50
  • I like this, never thought of using Mockito in CF! Mockio is what I use for all my Java mocking anyway. I'm going to give this a try tonight. – bittersweetryan Jul 14 '11 at 20:56
  • Sweet, this worked! Edward, would you mind if I blogged about this solution? I'll be sure to give you the credit you deserve. – bittersweetryan Jul 15 '11 at 03:03
0

I took Edward's answer and implemented it like so:

I used the JavaLoader library to create my mockito object. variables.cfEvernote = "";

variables.classLoader = createObject("component", "resources.JavaLoader").
    init(["#expandPath('../lib/mockito-all-1.8.5.jar')#",
          "#expandPath('../lib/CFEvernote.jar')#",                                                                      
          "#expandPath('../lib/libthrift.jar')#",
          "#expandPath('../lib/evernote-api-1.18.jar')#"]);  

variables.mockito = variables.classLoader.create("org.mockito.Mockito").init();

Then in the setup method of my munit test I create my new mock java object:

<cffunction name="setUp" access="public" output="false" returntype="void">
<cfscript>
variables.cfEvernote = createObject("component","com.714studios.cfevernote.CFEvernote").
                        Init(variables.configArray[1],variables.configArray[2],
                             "sandbox.evernote.com",
                             "http://localhost/cfevernote/callback.cfm"
                             "#ExpandPath('../lib')#");

variables.mockCFEvernote = variables.mockito.mock(variables.classLoader.create("com.sudios714.cfevernote.CFEvernote").
                           Init("123","S1","232","sandbox.evernote.com","mock").getClass());

variables.cfEvernote.setCFEvernote(mockCFEvernote);
</cfscript>     

Then in my tests I create my mock behavior like so.

<cffunction name="test..."  returntype="void" access="public" output="false" >
<cfscript>
    var notebooks = ""; 
    var expected = 12;
    var i = 0;
    var retArray = createObject("Java","java.util.ArrayList");
    var actual = "";

    for(i = 1; i lte 12; i = i + 1){
        retArray.Add("");
    }

    variables.mockito.when(mockCFEvernote.listNotebooks(12)).thenReturn(retArray);

    notebooks = variables.cfEvernote.getNotebooks(12); 

    actual = arrayLen(notebooks);

    assertEquals(expected,actual);
</cfscript>

I've also blogged about it in a bit more detail here - http://blog.bittersweetryan.com/2011/07/unit-testing-coldfusion-components-that.html.

bittersweetryan
  • 3,383
  • 5
  • 28
  • 42