Search code examples
backbone.jsunderscore.jsjasminesinonlodash

How to test LoDash debounce in Jasmine with Sinon fakeTimer?


I'm trying to write a test for debouncing user input in a search query. The function is defined on a Backbone View:

SearchView = Backbone.View.extend({
    events: {
        "input .search-input": "search"
    },

    // init, render, etc.

    search: _.debounce(function() {
        this.collection.fetch();   
    }, 200)
});

Originally, the Backbone library (v0.9.10) used Underscore (v1.4.4), and the test was defined as follows:

describe("SearchView", function() {
    var view, $viewContainer;

    beforeEach(function() {
        appendSetFixtures('<div class="jasmine-container"></div>');
        $viewContainer = $(".jasmine-container");

        view = new SearchView({
            el: $viewContainer
        });
    });

    afterEach(function() {
        view.remove();
        view.cleanup();
    });

    //...

    describe("wires the search input", function() {
        var collectionStub,
            fakeTimer;

        beforeEach(function() {
            collectionStub = sinon.stub(
                SearchResultsCollection.prototype,
                "fetch"
            );     

            fakeTimer = sinon.useFakeTimers();
        });

        afterEach(function() { 
            collectionStub.restore();
            fakeTimer.restore();
        });

        it("should not trigger a search before 200ms", function() {
            fakeTimer.tick(199);
            expect(collectionStub).not.toHaveBeenCalled();
        });

        it("should trigger a search after 200ms", function() {
            fakeTimer.tick(200);
            expect(collectionStub).toHaveBeenCalled();
        });
    });
});

However, now I want to incorporate LoDash instead of Underscore. Using the latest Underscore compatibility build on their site (LoDash 2.4.1 / Underscore 1.5.6), all my tests pass except for the one using _.debounce!

I did some research and came across these relevant issues to create a LoDash Underscore build with runInContext, but I have no idea how to use it due to lack of examples. How can I use _.runInContext() in my spec(s) to work with sinon.fakeTimer?


Solution

  • SearchView = Backbone.View.extend({
        events: {
            "input .search-input": function() {
                this.search();
            }
        },
    
        initialize: function() {
            this.search = _.debounce(this.search, 200);
        }
    
        // init, render, etc.
    
        search: function() {
            this.collection.fetch();   
        }
    });    
    
    describe("SearchView", function() {
        var view; 
        var $viewContainer;
        var clock;
        var lodash = window._;
    
        beforeEach(function() {
            appendSetFixtures('<div class="jasmine-container"></div>');
            $viewContainer = $(".jasmine-container");
    
            clock = sinon.useFakeTimers();
            window._ = _.runInContext(window);
    
            view = new SearchView({
                el: $viewContainer
            });
        });
    
        afterEach(function() {
            view.remove();
            view.cleanup();
    
            clock.restore();
            window._ = lodash;
        });
    
        //...
    
        describe("wires the search input", function() {
            var collectionStub;
    
            beforeEach(function() {
                collectionStub = sinon.stub(
                    SearchResultsCollection.prototype,
                    "fetch"
                );     
            });
    
            afterEach(function() { 
                collectionStub.restore();
            });
    
            it("should not trigger a search before 200ms", function() {
                fakeTimer.tick(199);
                expect(collectionStub).not.toHaveBeenCalled();
            });
    
            it("should trigger a search after 200ms", function() {
                fakeTimer.tick(200);
                expect(collectionStub).toHaveBeenCalled();
            });
        });
    });