Wednesday, November 28, 2012

Dealing With Nested Callback Sequences

I'm still new to this whole asynchronous/callback focused world, so in an effort to force myself to keep this blog going, I'm going to talk about one way I just worked out how to make it a bit less ugly.

My situation: I'm writing some tests for an object that updates its members when you call a function, and notifies the caller when it's done by using a callback. I want to test that a particular sequence of member updates happen after a certain number of function calls. This was my first attempt (I'm writing Node.js tests in TypeScript using Mocha, I've omitted the mocking I use to make the updates work but you should get the gist of it):
import assert = module("assert")

describe('Obj', function() {
    describe('#updateFunction()', function () {
        it('should work', function (done) {
            var obj = new Object();
            var expected1: { ... };
            var expected2: { ... };
            var expected3: { ... };

            // mock etc

            obj.updateFunction(() => {
                assert.deepEqual(expected1, obj.member);
                obj.updateNowPlaying(() => {
                    assert.deepEqual(expected2, obj.member);
                    obj.updateNowPlaying(() => {
                        assert.deepEqual(expected3, obj.member);
                        done();
                    });
                });
            });
        });
    });
});

Not great. Especially when I wanted to test a longer sequence of expectations.

Then I remembered a little thing called recursion and felt stupid for not thinking of it earlier:
import assert = module("assert")

describe('Obj', function() {
    describe('#updateFunction()', function () {

        var chainTestUpdateFunction = (obj: Object, expected: any[], done: () => void) => {
            if (expected.length == 0) {
                done();
            }
            else {
                obj.updateFunction(() => {
                    var curExpectation = expected.shift();
                    assert.deepEqual(curExpectation, obj.member);
                    chainTestUpdateFunction (station, expected, done);
                });
            }
        };

        it('should work', function (done) {
            var obj = new Object();
            var expected1: { ... };
            var expected2: { ... };
            var expected3: { ... };

            // mock etc
            var expected = [expected1, expected2, expected3];
            chainTestNowPlaying(obj, expected, done);
        });
    });
});

So, another pretty obvious solution that probably isn't even really worth blogging about. But, this is the internet after all.

No comments:

Post a Comment