CI与emberjs

时间:2012-09-07 11:26:49

标签: testing continuous-integration ember.js integration-testing travis-ci

我目前正在研究如何将基于ember.js的应用程序的测试套件集成到travis-ci中。首先,我们不是在开源服务上,而是将它用于私有存储库等。

我看了几个开源项目如何运行他们的ember.js测试套件,看起来他们设置了一个服务器,他们的项目可能会在有人推送到存储库时更新。 Then PhantomJS用于在该服务器上运行测试(实际上不在travis-ci本身上)。

我使用这种方法的问题是,这增加了另一个步骤(并最终增加了复杂性):我必须使用最新代码更新和维护服务器,以便我可以使用PhantomJS来运行测试套件。

另一个缺点是我没有看到它如何使我们能够测试PR(拉取请求)。必须使用来自PR的代码更新服务器。在PR合并之前测试PR是travis-ci的优点之一。

我无法找到关于仅通过CLI运行ember.js测试的任何内容 - 我希望有人在我面前解决这个问题。

1 个答案:

答案 0 :(得分:5)

我不能谈谈你关于travis-ci的问题......但是我可以提供一些关于使用jasmine对ember.js代码进行单元测试的想法。

在我开始使用ember.js之前,我使用jasmine和一个名为jasmine-node的简单node.js模块进行单元测试。这使我能够从命令行快速运行一套茉莉花单元测试,而无需打开浏览器或破解“js-test runner”/ etc

当我使用jasmine,jquery和简单的javascript模块时,我的工作很棒,我曾经保持我的javascript代码是人类可读的。但是当我需要使用ember / handlebars / etc时,茉莉花节点模块就掉了下来,因为它希望你在全局和窗口都有可用的东西。但是因为ember只是一个浏览器库而不是一切都在“全球”

我开始关注PhantomJS,就像你自己看不到自己增加复杂性一样。所以我决定周末去写茉莉花测试跑者空间里缺少的东西,而不是乱砍。我想要与jasmine-node相同的功能(意味着我在CI盒子上需要的只是最新版本的node.js和一个简单的npm模块来运行测试)

我写了一个名为jasmine-phantom-node的npm模块,其核心是使用node.js来运行phantomJS =>反过来,它会激活一个常规的jasmine html跑步者,并使用一个非常基本的快速网络应用程序来搜索测试结果的页面。

我花时间在github项目中放置了两个不同的示例,以便其他人可以看到它如何快速工作。这是固执己见,因此您需要在项目根目录中使用html文件,插件将使用该文件来执行测试。它还需要jasmine,jasmine-html以及最近的jQuery。

它为我个人解决了这个问题,现在我可以使用简单的jasmine编写针对ember的测试,并在没有浏览器的情况下从cmd行运行它。

这是一个茉莉花单元测试样本,我最近在这个测试运行时加入了一个ember视图。 Here是完整的ember / django项目的链接,如果你想看看应用程序中如何使用测试中的视图。

require('static/script/vendor/filtersortpage.js');
require('static/script/app/person.js');

describe ("PersonApp.PersonView Tests", function(){

  var sut, router, controller;

  beforeEach(function(){
    sut = PersonApp.PersonView.create();
    router = new Object({send:function(){}});
    controller = PersonApp.PersonController.create({});
    controller.set("target", router);
    sut.set("controller", controller);
  });

  it ("does not invoke send on router when username does not exist", function(){
    var event = {'context': {'username':'', 'set': function(){}}};
    var sendSpy = spyOn(router, 'send');
    sut.addPerson(event);
    expect(sendSpy).not.toHaveBeenCalledWith('addPerson', jasmine.any(String));
  });

  it ("invokes send on router with username when exists", function(){
    var event = {'context': {'username':'foo', 'set': function(){}}};
    var sendSpy = spyOn(router, 'send');
    sut.addPerson(event);
    expect(sendSpy).toHaveBeenCalledWith('addPerson', 'foo');
  });

  it ("does not invoke set context when username does not exist", function(){
    var event = {'context': {'username':'', 'set': function(){}}};
    var setSpy = spyOn(event.context, 'set');
    sut.addPerson(event);
    expect(setSpy).not.toHaveBeenCalledWith('username', jasmine.any(String));
  });

  it ("invokes set context to empty string when username exists", function(){
    var event = {'context': {'username':'foo', 'set': function(){}}};
    var setSpy = spyOn(event.context, 'set');
    sut.addPerson(event);
    expect(setSpy).toHaveBeenCalledWith('username', '');
  });
});

以下是我在上面进行单元测试的生产余烬视图

PersonApp.PersonView = Ember.View.extend({
  templateName: 'person',
  addPerson: function(event) {
    var username = event.context.username;
    if (username) {
      this.get('controller.target').send('addPerson', username);
      event.context.set('username', '');
    }
  }
});