Domanda

Im working on a test suite for an existing Backbone application using Jasmine and Sinon and I am testing that my router performs the correct actions on a certain route. Here's the actual route function:

favourites: function()
{
    //Dont re-initialize the favourites view as there is no need.
    //Instead, just render the favourite movies
    if ( ! this.favMoviesView)
    {
        this.favMoviesView = new cinephile.Views.FavouriteMoviesView({
            collection: cinephile.favouriteMovies
        });
    }
    else
    {
        this.favMoviesView.renderFavourites();
    }

    $('#content').html(this.favMoviesView.el);
},

In my test suite I want to assert that when navigating to to the favourites route this.favMoviesView will be created once and then, if it exists will not re-initialize but instead just call this.favMoviesView.renderFavourites() which is a method that iterates over the view's collection.

Here's my test spec:

describe('cinephile.Routers.CinephileRouter', function () {

    beforeEach(function () {

        this.router = new cinephile.Routers.CinephileRouter();
        this.routeSpy = sinon.spy();

        try
        {
            Backbone.history.start({ silent : true });
        }
        catch(e) {}

        this.router.navigate('elsewhere');

        this.favouritesViewStub = sinon.stub(cinephile.Views, 'FavouriteMoviesView')
            .returns(new Backbone.View());
    });

    afterEach(function () {
        this.favouritesViewStub.restore();
    });

    describe('Favourites Route', function() {

        it('should load the favourites on /favourites', function () {

            this.router.bind('route:favourites', this.routeSpy);
            this.router.navigate('favourites', true);

            expect(this.routeSpy.calledOnce).toBeTruthy();
            expect(this.routeSpy.calledWith()).toBeTruthy();
        });

        it('creates a favourites view if one doesn\'t exist', function () {
            this.router.favourites();
            expect(this.favouritesViewStub.calledOnce).toBeTruthy();
        });

        it('Reuses the favourites view if one does exist and reset it\'s collection', function () {
            this.router.favourites();
            this.router.favourites();

            expect(this.favouritesViewStub.calledOnce).toBeTruthy();
            expect(this.favouritesViewStub.renderFavourites).toHaveBeenCalledTwice();
        }); 
    });
});

My first two tests pass and I believe them to correctly describe the favourites method in my router. The third test is the the one giving me problems. As I understand it, because I am testing my router and NOT the FavouriteMoviesView I should be stubbing out the view to keep the test isolated. If that is the correct assumption, my issue becomes that the stub won't have a renderFavourites method as it is a stubbed out Backbone.View().

How can I fix this particular problem and if you are so inclined, I believe I'm missing something conceptual so feel free to explain what it is that I'm not understanding.

Cheers.

È stato utile?

Soluzione

You problem is that you want to mock something inside a mock function. What I would suggest is that instead of this...

this.favouritesViewStub = sinon.stub(cinephile.Views, 'FavouriteMoviesView').returns(new Backbone.View());

...have this:

var StubView = Backbone.View.extend({
  renderFavourites: sinon.stub()
});
this.favouritesViewStub = sinon.stub(cinephile.Views, 'FavouriteMoviesView').returns(new StubView());

Now your View "constructor" will return a StubView, which has the method you are calling stubbed out. So this Backbone View with the stubbed out method will be placed in the router.favMoviesView -property. The favouritesViewStub property still contains just the "constructor" -function, so you can't access this stubbed method from there. This is why you haveto change this from the last test:

expect(this.favouritesViewStub.renderFavourites).toHaveBeenCalledTwice();

to something like this:

expect(this.router.favMoviesView.renderFavourites).toHaveBeenCalledTwice();

This way you will actually check if the router's copy of the view has had the method called twice.

Hope this works for you, comment if it doesn't! I didn't test this out, so there could be some problems, but I'm sure the logic behind works.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top