Skip to content

Instantly share code, notes, and snippets.

@lucasdavila
Last active August 29, 2015 14:21
Show Gist options
  • Save lucasdavila/385e3b12175a0934569d to your computer and use it in GitHub Desktop.
Save lucasdavila/385e3b12175a0934569d to your computer and use it in GitHub Desktop.
Backbone.js router testing
/*global define*/
define([
'routes/base',
'jquery',
'views/group_users'
], function (BaseRouter, $, GroupUsersView) {
'use strict';
var GroupUserRouter = BaseRouter.extend({
routes: {
'groups' : 'index'
},
index: function() {
var view = new GroupUsersView(),
container = $('#app-container');
container.html(view.render().$el);
}
});
return GroupUserRouter;
});
/*global define, describe, it, expect */
define([
'routes/group_user'
],function(Router) {
'use strict';
describe('GroupUserRouter', function() {
var subject;
beforeEach(function() {
subject = new Router();
});
describe('#routes', function(){
it('routes groups to index', function() {
expect(subject.routes.groups).to.be.eq('index');
});
});
describe('#index', function() {
beforeEach(function() {
// levando em consideração que não é possivel adicionar um spy (stub) para window.foo
// como nos exemplos abaixo, pois o projeto usa require.js para carregar dependencias.
//
// this.viewStub = sinon.stub(window, 'GroupUsersView').returns(new Backbone.View());
// this.jqueryStub = sinon.stub(window, '$');
// duvida 1: como "stubar" o resultado do metodo render de uma instância da classe GroupUsersView?
// duvida 2: como "stubar" um elemento jquery com seletor "#app-container"?
});
it('renders the resources view', function() {
subject.index();
// duvida 3: como testar que stud do elemento jquery,
// teve o metodo ".html" chamado com o "(stub da instância da view).$el"?
});
});
});
});
@lucasdavila
Copy link
Author

Adicionalmente eu tentei usar a biblioteca squire.js para mockar as chamadas a 'views/group_users' e 'queryj' via require.js, sem sucesso.

Talvez eu não tenha entendido exatamente para que o squire.js serve, o que eu tentei foi o seguinte:

/*global define, describe, it, expect, sinon */

define([
    'routes/group_user',
    'squire'
],function(Router, Squire) {
    'use strict';

    describe('GroupUserRouter', function() {

        var subject,
            injector = new Squire();

        beforeEach(function() {
            subject = new Router();
        });

        describe('#routes', function(){
            it('routes groups to index', function() {
                expect(subject.routes.groups).to.be.eq('index');
            });
        });

        describe('#index', function() {
            beforeEach(function() {
                this.viewStub = sinon.stub();

                injector.mock('views/group_users', this.viewStub);
              });

            it('renders the resources view', function() {
                subject.index();

                expect(this.viewStub.calledWithNew()).to.be.true();
            });
        });

    });

});

Porem os testes falham com: AssertionError: expected false to be true.

Eu também tentei realizar o mock injector.mock('views/group_users', this.viewStub); antes de carregar o router routes/group_user com o método require, mas o teste falhou da mesma maneira.

@lucasdavila
Copy link
Author

O Renan Carvalho me deu uma dica de não testar o roteador unitariamente e sim realizar testes de integração, eu também cogitei fazer isto.

Mas de qualquer modo a dúvida sobre como realizar stub de dependências carregadas pelo require.js continua, eu acredito que poderei ter esta necessidade em outros testes.

@renancarvalho
Copy link

@lucasdavila, Como vc mencionou eu geralmente testo minhas rotas com testes de integração, uma vez que tento ter o minimo de lógica possível no router.
Olhando seu código eu percebi alguns pontos:

1 - Creio que um stub não seja adequado no seu cenario uma vez que sua asserção esta baseada em comportamento(spy ou mocks) não em estado(stub ou mocks talvez..)

2 - Eu carregaria todas as dependências do modulo de uma vez, no topo do arquivo.
exemplo

define([
    'routes/group_user',
    'squire',
    'views/group_users'
],function(Router, Squire, GroupUsersView) { 
... / resto do codigo /...

describe('#index', function() {
            beforeEach(function() {
               // spy na view, como o initialize so vai existir e ser chamado quando a view for instanciada, e não temos nenhuma instancia dela, temos que usar o prototype.
               var spy = sinon.spy(GroupUsersView.prototype, "initialize");
              });

            it('renders the resources view', function() {
                subject.index();//essa function quando invocada vai instanciar a view.

                expect(spy).to.HaveBeenCalled();
                 //existem outros tipos de asserts, ve o que vc quer :)
            });
        });

Ps: eu não seu se minha sintaxe com o sinon esta certa, uso mais o Jasmine, mas acho que o caminho é mais por ali.

abraços

@lucasdavila
Copy link
Author

@renancarvalho valeu, vou testar sua sugestão :)

@lucasdavila
Copy link
Author

@renancarvalho, a tua sugestão funcionou (abaixo segue código funcional), isto permite que eu teste se a view foi inicializada, e tambem se outros metodos foram chamados.

Mas isto não faz exatamente o que eu queria, que seria testar se o elemento jquery, teve o metodo .html chamado com o $el da view como argumento.

Fiz algumas tentativas sem sucesso, para "stubar" o elemento jquery e a instância da view, no ruby com Rspec isto seria bem fácil, provavelmente eu que não sei fazer em JS com sinon.js.

Como isto ficou bem trabalhoso, acho que por hora vou decidir apenas por realizar os testes de integração, sem testes de roteador. 😔

Obrigado pela ajuda :)

/*global define, describe, it, expect, sinon */

define([
    'routes/group_user',
    'views/group_users'
],function(Router, GroupUsersView) {
    'use strict';

    describe('GroupUserRouter', function() {

        var subject;

        beforeEach(function() {
            subject = new Router();
        });

        describe('#routes', function(){
            it('routes groups to index', function() {
                expect(subject.routes.groups).to.be.eq('index');
            });
        });

        describe('#index', function() {
            beforeEach(function() {
                this.initializeSpy = sinon.spy(GroupUsersView.prototype, 'initialize');
            });

            it('renders the resources view', function() {
                subject.index();

                expect(this.initializeSpy.calledOnce).to.be.true;
            });
        });

    });

});

@renancarvalho
Copy link

Entendi, tbm da pra fazer um spy na function html do jQuery, pra isso basta carrega-lo no topo do arquivo, e fazer um spy parecido com (jquery, 'html').

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment