1

What is the best way to test for event handling in objects? I can't seem to figure this one out.

I have an object that sets up a few event listeners and when it observes these events being fired it changes a dom object on the page. When I have multiple test going the last one in the example below fails, if I comment out the others it's fine.

I have a test suite that looks like

var TitleTest = TestCase('TitleTest');

TitleTest.prototype.defaultTitle = 'title';
TitleTest.prototype.defaultCount = '0';

TitleTest.prototype.setUp = function() {
    var titleObj;
    this.div = new Element('div');
    $$('body').first().insert(this.div);
    this.div.insert('<div id="title"><h1><span id="titleCaption">' + this.defaultTitle + '</span><span id="titleCount">' + this.defaultCount + '</span></h1></div>');
    titleObj = new Title();
});

TitleTest.prototype.testNewItemsEvent = function() {
    var data = {count: 10};

    assertEquals('Count should be zero before events are fired', this.defaultCount, $('titleCount').innerHTML);
    document.fire('custom:NewItems', data);
    assertEquals('New count should be 10', data.count + '', $('titleCount').innerHTML);
});

// ... a few other simple tests like the one above

TitleTest.prototype.testUpdateSpecial = function() {
    var data = {caption: 'Special Title', count: 10},
        specialObj = {special: {type: 2, value: 5}};

    // Emulate a special type of category, that can only be 
    // added at page load
    document.fire('custom:UpdateTitle', data);
    assertEquals(data.caption, $('titleCaption').innerHTML);
    assertEquals(data.count, $('titleCount').innerHTML);

    //Removing the special category should revert the title to its default
    document.fire('custom:RemoveSpecial', specialObj);
    assertEquals(this.defaultTitle, $('titleCaption').innerHTML);
    assertEquals(this.defaultCount, $('titleCount').innerHTML);

    // only way to get this added back in during non load is using
    // javascript history so it needs to revert to previous state
    document.fire('custom:AddSpecial', specialObj);
    assertEquals(data.caption, $('titleCaption').innerHTML);
    assertEquals(data.count + '', $('titleCount').innerHTML);
});

The last pair of asserts will always fail if the previous tests run, but will pass if the I comment out the other tests. I'm at a loss what I can do to get this working.

Edit: here's the code that handles the add/remove of special titles

    Event.observe(document, 'custom:UpdateTitle', function(event) {
        if (event.memo.caption) {
            this._updateCaption(event.memo.caption);
        }

        if (event.memo.count) {
            this._updateCount(event.memo.count);
        }   

    }.bind(this));


    Event.observe(document, 'custom:RemoveSpecial', function(event) {
        if (
            event.memo.special.type === 1 ||
            event.memo.special.type === 2 ||
            (   
                event.memo.special.type === 3 &&
                parseInt(event.memo.special.value, 10) === 8
            )   
        ){  
            this._previousTitle = $('titleCaption').innerHTML;
            this._resetTitle();
        }   
    }.bind(this));

    Event.observe(document, 'custom:AddSpecial', function(event) {
        if (
            event.memo.special.type === 1 ||
            event.memo.special.type === 2 ||
            (   
                event.memo.special.type === 3 &&
                parseInt(event.memo.special.value, 10) === 8
            )   
        ){  
            if (!this._previousTitle.blank()) {
                this._updateCaption(this._previousTitle);
            }   
        }   
    }.bind(this));
JaredMcAteer
  • 21,688
  • 5
  • 49
  • 65
  • Can you show what happens when your object handles the `AddSpecial` custom event? Have you tried to make a separate test for the `AddSpecial` event? Also, apparently, there is a mistake here - you assert both the caption and the title to be equal to `data.caption` in the first batch of assertions. – Igor Zinov'yev Jul 07 '11 at 06:04
  • RE: `data.caption` just typo whilst removing extra code that isn't applicable. Adding the code that handles the events in an edit. – JaredMcAteer Jul 07 '11 at 13:34

2 Answers2

1

Try writing some cleanup code in a tearDown method to detach the titleObj and its listeners from the DOM. As far as I know jsTestDriver resets the DOM after each test, but I'm not sure what happens with objects listening to DOM events. You might still have an old titleObj around that can't be garbage collected because it's attached to the DOM.

Other than that, the way you test DOM events looks fine. Fire the DOM event and assert if the code did what it's supposed to...

meyertee
  • 2,211
  • 17
  • 16
  • Typo was just here. I force a refresh of the browser with the `--reset` command flag which returns it to it's clean state when the tests finish but it does not reset it during the tests. Unfortunately tear down is also fired after all the tests are finished. – JaredMcAteer Jul 06 '11 at 13:38
  • 1
    The tearDown method of your TestCase definitely fires after each individual test. I'd give that a try - detach the titleObj in tearDown and see if that fixes it. – meyertee Jul 07 '11 at 07:27
1

@meyertee alluded to the solution of my problem. I assumed that setup and teardown worked like PHPUnit where they are respectively fired only before and after of all the tests have run but jsTestDriver fires them before and after each test. Since I was setting up my DOM and Title event in the setup, a new title object was being created before each test and thus more listeners causing a race condition. I moved instantiation of my Title object out of the setup and this has fixed the issue.

JaredMcAteer
  • 21,688
  • 5
  • 49
  • 65