在backbone.js中测试路由器是否正确?

Ind*_*ial 31 javascript backbone.js jasmine sinon

所以我刚刚开始使用sinon.js&为我正在进行的javascript应用程序编写测试jasmine.js.总体上工作得很好,但我还需要能够测试我的路由器.

处于当前状态的路由器将触发许多视图和其他内容,jasmine.js通过调用Backbone.navigate依赖于应用程序状态和UI itneraction来终止当前测试.

那么我怎样才能测试到不同位置的路由是否可行,同时保持路由器"沙盒化"并且不允许它们改变路由?

我可以设置一些监视pushState更改或类似的模拟函数吗?

ggo*_*zad 37

这是使用jasmine执行此操作的低级方法,测试pushState是否按预期工作,并且您的路由器正确设置了...我假设router已经初始化并且将路由映射到''.您可以根据其他路线进行调整.我还假设您已完成应用初始化aBackbone.history.start({ pushState: true });

    describe('app.Router', function () {

        var router = app.router, pushStateSpy;

        it('has a "home" route', function () {
            expect(router.routes['']).toEqual('home');
        });

        it('triggers the "home" route', function () {
            var home = spyOn(router, 'home').andCallThrough();
            pushStateSpy = spyOn(window.history, 'pushState').andCallFake(function (data, title, url) {
                expect(url).toEqual('/');
                router.home();
            });
            router.navigate('');
            expect(pushStateSpy).toHaveBeenCalled();
            expect(home).toHaveBeenCalled();
            ...
        });
    });  
Run Code Online (Sandbox Code Playgroud)

你可以通过这样做来有效地实现类似的事情Backbone.history.stop();.

更新:浏览器没有pushState:

如果您测试的浏览器支持,这当然可以正常工作pushState.如果您针对不支持的浏览器进行测试,则可以按如下方式进行有条件的测试:

it('triggers the "home" route', function () {
    var home = spyOn(router, 'home').andCallThrough();

    if (Backbone.history._hasPushState) {
        pushStateSpy = spyOn(window.history, 'pushState').andCallFake(function (data, title, url) {
            expect(url).toEqual('/');
            router.home();
        });
        router.navigate('', {trigger: true});
        expect(pushStateSpy).toHaveBeenCalled();
        expect(home).toHaveBeenCalled();

    } else if (Backbone.history._wantsHashChange) {
        var updateHashSpy = spyOn(Backbone.history, '_updateHash').andCallFake(function (loc, frag) {
            expect(frag).toEqual('');
            router.home();
        });
        router.navigate('', {trigger: true});
        expect(updateHashSpy).toHaveBeenCalled();
        expect(home).toHaveBeenCalled();
    }
});
Run Code Online (Sandbox Code Playgroud)

如果你在IE6上,祝你好运.


Chr*_*nes 8

当我测试骨干路由器时,我关心的是我提供的路由是使用正确的参数调用我指定的函数.这里的很多其他答案都没有真正测试过.

如果需要测试某些路由的功能,可以自行测试这些功能.

假设你有一个简单的路由器:

App.Router = Backbone.Router.extend({
  routes: {
    '(/)':'index',
    '/item/:id':'item'
  },
  index: {
    //render some template
  }, 
  item: {
    //render some other template, or redirect, or _whatever_
  }
});
Run Code Online (Sandbox Code Playgroud)

我是这样做的:

describe('Router', function() {

  var trigger = {trigger: true};
  var router

  beforeEach(function() {
    // This is the trick, right here:
    // The Backbone history code dodges our spies
    // unless we set them up exactly like this:
    Backbone.history.stop(); //stop the router
    spyOn(Router.prototype, 'index'); //spy on our routes, and they won't get called
    spyOn(Router.prototype, 'route2'); 

    router = new App.Router(); // Set up the spies _before_ creating the router
    Backbone.history.start();
  });

  it('empty route routes to index', function(){
    Backbone.history.navigate('', trigger);
    expect(router.index).toHaveBeenCalled();
  });

  it('/ routes to index', function(){
    router.navigate('/', trigger);
    expect(router.index).toHaveBeenCalled();
  });

  it('/item routes to item with id', function(){
    router.navigate('/item/someId', trigger);
    expect(router.item).toHaveBeenCalledWith('someId');
  });
});
Run Code Online (Sandbox Code Playgroud)


Ind*_*ial 4

这是我自己最终使用的。我通过扩展路由器并使用空白方法覆盖方法来制作路由器的模拟版本,以防止它在被调用时调用任何进一步的逻辑:

describe("routers/main", function() {

    beforeEach(function() {

        // Create a mock version of our router by extending it and only overriding
        // the methods
        var mockRouter = App.Routers["Main"].extend({
            index: function() {},
            login: function() {},
            logoff: function() {}
        });

        // Set up a spy and invoke the router
        this.routeSpy = sinon.spy();
        this.router = new mockRouter;

        // Prevent history.start from throwing error
        try {
            Backbone.history.start({silent:true, pushState:true});
        } catch(e) {

        }

        // Reset URL
        this.router.navigate("tests/SpecRunner.html");
    });

    afterEach(function(){
        // Reset URL
        this.router.navigate("tests/SpecRunner.html");
    });

    it('Has the right amount of routes', function() {
        expect(_.size(this.router.routes)).toEqual(4);
    });

    it('/ -route exists and points to the right method', function () {
        expect(this.router.routes['']).toEqual('index');
    });

    it("Can navigate to /", function() {
        this.router.bind("route:index", this.routeSpy);
        this.router.navigate("", true);
        expect(this.routeSpy.calledOnce).toBeTruthy();
        expect(this.routeSpy.calledWith()).toBeTruthy();
    });

});
Run Code Online (Sandbox Code Playgroud)

请注意,sinon.js上面用于创建间谍并underscore.js提供该size功能。