Wednesday, 4 December 2013

Unit Testing / TDD - off()

G'day:

As alluded to in my earlier article, I initially forgot that as well as a way to bind event handlers (on()) to an event, and trigger() the event, I also really need a way to unbind handlers from an event. Which I am going to implement the same way JQuery does: with an off() function.

As has been my wont, here is a list of the previous articles in this series:
And all the code is on Github, here: https://github.com/daccfml/scratch/tree/master/blogExamples/unittests/events


Random thought...

G'day:
Right, I've been at the pub for the third night running (so much for a low-expense Dec), so this is not exactly the most well-formed idea I have ever had.

Railo supports array types

G'day:
This might be old news, but it's something I didn't know until today. Railo supports function return values and arguments of type array-of-something, eg:

function acceptArrayOfSamples(required Sample[] samples){
    // etc
}

Sample[] function returnArrayOfSamples(){
    return [new Sample(), new Sample()];
}

Where sample is a CFC, eg:


//Sample.cfc
component {

}

It also works (kinda) for inbuilt types too, like strings and numerics.

I had a look at how well this stuff works, writing these test functions:

// functionsToTest.cfm
private any function acceptArrayOfSamples(required Sample[] samples){
    return samples;
}

private Sample[] function returnArrayOfSamples(required array samples){
    return samples;
}

private any function acceptArrayOfStrings(required string[] strings){
    return strings;
}

private string[] function returnArrayOfStrings(required array strings){
    return strings;
}

private any function acceptArrayOfNumerics(required numeric[] numerics){
    return numerics;
}

private numeric[] function returnArrayOfNumerics(required array numerics){
    return numerics;
}

private any function acceptArrayOfStructs(required struct[] structs){
    return structs;
}

private struct[] function returnArrayOfStructs(required array structs){
    return structs;
}

These functions either accept an array of various types (Samples, strings, numerics, structs), or return same.

I've written some unit tests to see how well this lot works:

// TestArraysOfObjects.cfc
component extends="mxunit.framework.TestCase" {


    public void function beforeTests(){
        variables.arrayOfSamples    = [new Sample(), new Sample()];
        variables.arrayofSubSamples    = [new SubSample(), new SubSample()];
        variables.arrayofNotSamples    = [new NotSample(), new NotSample()];
        variables.arrayOfStrings    = ["array", "of", "strings"];
        variables.arrayOfNumerics    = [-1, 2.2, pi()];
        variables.arrayOfStructs    = [{one="tahi"}, {two="rua"}, {three="toru"}, {four="wha"}];

        include "./functionsToTest.cfm";
    }


    public void function testAcceptArrayOfSamples(){
        acceptArrayOfSamples(arrayOfSamples);
    }

    public void function testReturnArrayOfSamples(){
        returnArrayOfSamples(arrayOfSamples);
    }

    /**
    * @mxunit:expectedexception expression
    */ 
    public void function testAcceptArrayOfSamples_withStrings(){
        acceptArrayOfSamples(arrayOfStrings);
    }

    /**
    * @mxunit:expectedexception expression
    */ 
    public void function testReturnArrayOfSamples_withStrings(){
        returnArrayOfSamples(arrayOfStrings);
    }

    public void function testAcceptArrayOfSamples_withSubSamples(){
        acceptArrayOfSamples(arrayOfSubSamples);
    }

    public void function testReturnArrayOfSamples_withSubSamples(){
        returnArrayOfSamples(arrayOfSubSamples);
    }
    
    /**
    * @mxunit:expectedexception expression
    */ 
    public void function acceptArrayOfSamples_withNotSamples(){
        acceptArrayOfSamples(arrayOfNotSamples);
    }

    /**
    * @mxunit:expectedexception expression
    */ 
    public void function testReturnArrayOfSamples_withNotSamples(){
        returnArrayOfSamples(arrayOfNotSamples);
    }

    public void function testAcceptArrayOfStrings(){
        acceptArrayOfStrings(arrayOfStrings);
    }

    public void function testReturnArrayOfStrings(){
        returnArrayOfStrings(arrayOfStrings);
    }

    public void function testAcceptArrayOfNumerics(){
        acceptArrayOfNumerics(arrayOfNumerics);
    }

    public void function testReturnArrayOfNumerics(){
        returnArrayOfNumerics(arrayOfNumerics);
    }

    /**
    * @mxunit:expectedexception expression
    */ 
    public void function testAcceptArrayOfNumerics_withStrings(){
        acceptArrayOfNumerics(arrayOfStrings);
    }

    /**
    * @mxunit:expectedexception expression
    */ 
    public void function testReturnArrayOfNumerics_withStrings(){
        returnArrayOfNumerics(arrayOfStrings);
    }

    public void function testAcceptArrayOfStructs(){
        acceptArrayOfStructs(arrayOfStructs);
    }

    public void function testReturnArrayOfStructs(){
        returnArrayOfStructs(arrayOfStructs);
    }

}

Where Sample.cfc is as per above, and SubSample.cfc and NotSample.cfc are as follows:

// SubSample.cfc
component extends="Sample" {

}

// NotSample.cfc
component {
    
}

To summarise the tests, what I've done is:
  • for the functions expecting/returning a Sample array, passed in arrays of Samples, SubSamples, NotSamples, strings. The latter two are expected to error, and do;
  • for the functions expecting a string array, just tested with a string array
  • for the functions expecting a numeric array, tested with both a numeric array and a string array (the latter ones - correctly - error)
  • for the functions expecting a struct array, tested just with a struct array
The results were as follows:


So the following tests fail:
  • passing an array of strings to a function expecting... an array of strings;
  • same with the function expecting a numeric array, it fails when it correctly receives an array of numerics;
  • same with structs.
Oddly, it's only when passing 'em in that there's a problem: it sees the arrays as the correct types when returning them.

Update:

As Rory observes in his comment below: this has all been fixed in Lucee. Nice one.

This is a handy feature, but it's incomplete. I've gotta go do some work now, but I'll check if there's a bug report for this, and raise one if not. And I will cross-reference back here either way. I'll also check to see if there's a ticket to implement this in ColdFusion, and raise / cross-reference accordingly.

That's it.

--
Adam

Sunday, 1 December 2013

CFML: Discussion with a former Java developer

G'day:
This is an adjunct to my most recent TDD article: "Unit Testing / TDD - passing data to on() and trigger()".

A bloke at the pub walked past my screen and saw my code, and asked me what I was up to. It turns out he was a developer too, working in Java (well: he was mostly doing QA stuff via Selenium these days, but still in Java).

I said to him I worked with ColdFusion, and he was like "oh yeah, I've heard of that. My boss knew something about it. Is it still around?" I've heard other people report this reaction a lot, but I've never really experienced it myself. But now I have.

A while later after the rugby finished and after I'd finished writing the article, we got to talking again, and I gave him the run-down of what ColdFusion / CFML was, and the Allaire / Macromedia / Adobe history was. He was very surprised that Adobe were in the business of selling a programming language, as it didn't seem much of a fit with the rest of their business model. He was also very bloody surprised that anyone would sell a programming language (ie: expect people to part with money for a language), and was gobsmacked that it costs around  €7000. I explained it came with an app server, but this didn't mollify him one bit: he looked at me like I had sprouted a second head.

He wanted to know why anyone would buy it if it ran on the JVM anyhow: why not use Java? I pointed out a lot of languages other than Java use the JVM, and cited that ColdFusion's USP was its ease of use. I cited this as an example:

Unit Testing / TDD - passing data to on() and trigger()

G'day:
Yesterday I plugged through more test/code and got the code to the point that it would bind and trigger event handlers A-OK. However we still need to update the code so that we can pass data at both bind-time and trigger-time to the handler's execution.

Before I continue, here's the index to the articles in this series I've already written:

Friday, 29 November 2013

Unit Testing / TDD - continuing the tests for on() and trigger()

G'day:
I was getting into a rhythm with my TDD cycle this afternoon... test... refine... test... refine... here's what I was doing. Well: after the obligatory recap links (/SEO bait):

Do you know what? I am actually enjoying writing this code. And I'm now past the bits I already knew I had to write (and had the code pretty much already written in my head), so I'm doing really really TDD now. On with the show...

Unit Testing / TDD - getting stuck on how / what to test (part 2/2)

G'day:
Today I'm resuming where I left off yesterday, but first the obligatory links to the rest of the series so far:
Yesterday I questioned how thoroughly I should test return values from functions. Today I am completely flummoxed as to how I test something at all. Spoiler warning: I never worked it out.