简体   繁体   English

让requirejs与Jasmine一起工作

[英]Getting requirejs to work with Jasmine

I first want to say that I am new to RequireJS and even newer to Jasmine. 我首先想说的是我是RequireJS的新手,甚至是Jasmine的新手。

I am having some issues with the SpecRunner and require JS. 我有一些SpecRunner的问题,需要JS。 I have been following the tutorials of Uzi Kilon and Ben Nadel (along with some others) and they helped some but I am still having some issues. 我一直在关注Uzi Kilon和Ben Nadel(以及其他一些人)的教程,他们帮助了一些,但我仍然遇到了一些问题。

It seems that, if there is an error that is thrown in the test (I can think of one in particular, a type error) the spec runner html will display. 看来,如果在测试中抛出错误(我可以想到一个特别是类型错误),将显示spec runner html。 This tells me that I have some issues in the javascript. 这告诉我,我在javascript中有一些问题。 However, after I fix those error no HTML is displayed anymore. 但是,在我修复这些错误后,不再显示HTML。 I cannot get the test runner to display at all. 我根本无法让测试跑者显示出来。 Can someone find something wrong with my code that would cause this issue? 有人发现我的代码可能会导致此问题吗?

Here is my directory structure : 这是我的目录结构

Root 
|-> lib
    |-> jasmine
        |-> lib (contains all of the jasmine lib)
        |-> spec
        |-> src
    |-> jquery (jquery js file)
    |-> require (require js file) 
index.html (spec runner) specRunner.js

Here is the SpecRunner (index) HTML : 这是SpecRunner(索引)HTML

<!doctype html>
<html lang="en">
    <head>
        <title>Javascript Tests</title>

        <link rel="stylesheet" href="lib/jasmine/lib/jasmine.css">

        <script src="lib/jasmine/lib/jasmine.js"></script>
        <script src="lib/jasmine/lib/jasmine-html.js"></script>
        <script src="lib/jquery/jquery.js"></script>
        <script data-main="specRunner" src="lib/require/require.js"></script>

        <script>
            require({ paths: { spec: "lib/jasmine/spec" } }, [
                    // Pull in all your modules containing unit tests here.
                    "spec/notepadSpec"
                ], function () {
                    jasmine.getEnv().addReporter(new jasmine.HtmlReporter());
                    jasmine.getEnv().execute();
                });
        </script>

    </head>

<body>
</body>
</html>

Here is the specRunner.js (config) 这是specRunner.js(config)

require.config({
    urlArgs: 'cb=' + Math.random(),
    paths: {
        jquery: 'lib/jquery',
        jasmine: 'lib/jasmine/lib/jasmine',
        'jasmine-html': 'lib/jasmine/lib/jasmine-html',
        spec: 'lib/jasmine/spec/'
    },
    shim: {
        jasmine: {
            exports: 'jasmine'
        },
        'jasmine-html': {
            deps: ['jasmine'],
            exports: 'jasmine'
        }
    }
});

Here is a spec: 这是一个规范:

require(["../lib/jasmine/src/notepad"], function (notepad) {
    describe("returns titles", function() {
        expect(notepad.noteTitles()).toEqual("");


    });
});

The notepad source: 记事本来源:

define(['lib/jasmine/src/note'], function (note) {

    var notes = [
        new note('pick up the kids', 'dont forget to pick  up the kids'),
        new note('get milk', 'we need two gallons of milk')
    ];


    return {
        noteTitles: function () {
            var val;

            for (var i = 0, ii = notes.length; i < ii; i++) {
                //alert(notes[i].title);
                val += notes[i].title + ' ';
            }

            return val;
        }
    };
});

And the Note source (JIC): 而Note源(JIC):

define(function (){
    var note = function(title, content) {
        this.title = title;
        this.content = content;
    };

    return note;
});

I have made sure that, as far as the app is concerned, the paths are correct. 我已经确定,就应用程序而言,路径是正确的。 Once I get this working I can play with configuring that paths so that it isn't so yucky. 一旦我开始工作,我就可以配置这些路径,这样它就不那么令人讨厌了。

I managed to get this working with some trial and error. 我设法通过一些试验和错误来解决这个问题。 The main issue was that when you write specs it isn't a require that you want to create, you want to use define: 主要问题是当你编写规范时,它不是你想要创建的要求,你想使用define:

Original: 原版的:

require(["/lib/jasmine/src/notepad"], function (notepad) {
    describe("returns titles", function() {
        expect(notepad.noteTitles()).toEqual("pick up the kids get milk");


    });
});

Working: 工作:

define(["lib/jasmine/src/notepad"], function (notepad) {
    describe("returns titles", function () {

        it("something", function() {

            expect(notepad.noteTitles()).toEqual("pick up the kids get milk ");
        });

    });
});

After doing some research it became clear that, when using RequireJS, Anything that you want the require() to use must be wrapped in a define (seems obvious now I guess). 经过一些研究后,很明显,当使用RequireJS时,任何你想要require()的东西都必须包含在一个定义中(现在我觉得很明显)。 You can see that, in the specRunner.js file, a require is used when executing the tests (therefore the need to "define" the specs. 您可以看到,在specRunner.js文件中,在执行测试时使用了需求(因此需要“定义”规范。

The other issue is that, when creating specs, the describe() AND the it() are necessary (not just the describe like I had in the posted example). 另一个问题是,在创建规范时,describe()和it()是必要的(不仅仅是我在发布的示例中所描述的描述)。

Original: 原版的:

describe("returns titles", function() {
        expect(notepad.noteTitles()).toEqual("pick up the kids get milk");


    });

Working: 工作:

describe("returns titles", function () {

        it("something", function() {

            expect(notepad.noteTitles()).toEqual("pick up the kids get milk ");
        });

    });

I also changed around where the test runner exists but this was a refactor and did not change the outcome of the tests. 我也改变了测试运行器存在的位置,但这是一个重构,并没有改变测试的结果。

Again, here are the files and the changed: 同样,这里是文件和更改:

note.js: stayed the same note.js:保持不变

notepad.js: stayed the same notepad.js:保持不变

index.html: index.html的:

<!doctype html>
<html lang="en">
    <head>
        <title>Javascript Tests</title>
        <link rel="stylesheet" href="lib/jasmine/lib/jasmine.css">
        <script data-main="specRunner" src="lib/require/require.js"></script>
    </head>

    <body>
    </body>
</html>

specRunner.js: specRunner.js:

require.config({
    urlArgs: 'cb=' + Math.random(),
    paths: {
        jquery: 'lib/jquery',
        'jasmine': 'lib/jasmine/lib/jasmine',
        'jasmine-html': 'lib/jasmine/lib/jasmine-html',
        spec: 'lib/jasmine/spec/'
    },
    shim: {
        jasmine: {
            exports: 'jasmine'
        },
        'jasmine-html': {
            deps: ['jasmine'],
            exports: 'jasmine'
        }
    }
});


require(['jquery', 'jasmine-html'], function ($, jasmine) {

    var jasmineEnv = jasmine.getEnv();
    jasmineEnv.updateInterval = 1000;

    var htmlReporter = new jasmine.HtmlReporter();

    jasmineEnv.addReporter(htmlReporter);

    jasmineEnv.specFilter = function (spec) {
        return htmlReporter.specFilter(spec);
    };

    var specs = [];

    specs.push('lib/jasmine/spec/notepadSpec');



    $(function () {
        require(specs, function (spec) {
            jasmineEnv.execute();
        });
    });

});

notepadSpec.js: notepadSpec.js:

define(["lib/jasmine/src/notepad"], function (notepad) {
    describe("returns titles", function () {

        it("something", function() {

            expect(notepad.noteTitles()).toEqual("pick up the kids get milk");
        });

    });
});

Just adding this as an alternate answer for people who are you using Jasmine 2.0 standalone. 只需将此添加为您使用Jasmine 2.0独立版的人的替代答案。 I believe this can work for Jasmine 1.3 also, but the async syntax is different and kind of ugly. 我相信这也适用于Jasmine 1.3,但异步语法不同而且有点难看。

Here is my modified SpecRunner.html file: 这是我修改过的SpecRunner.html文件:

<!DOCTYPE HTML>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <title>Jasmine Spec Runner v2.0.0</title>

  <link rel="shortcut icon" type="image/png" href="lib/jasmine-2.0.0/jasmine_favicon.png">
  <link rel="stylesheet" type="text/css" href="lib/jasmine-2.0.0/jasmine.css">

  <!-- 
  Notice that I just load Jasmine normally
  -->    
  <script type="text/javascript" src="lib/jasmine-2.0.0/jasmine.js"></script>
  <script type="text/javascript" src="lib/jasmine-2.0.0/jasmine-html.js"></script>
  <script type="text/javascript" src="lib/jasmine-2.0.0/boot.js"></script>

  <!-- 
  Here we load require.js but we do not use data-main. Instead we will load the
  the specs separately. In short we need to load the spec files synchronously for this
  to work.
  -->
  <script type="text/javascript" src="js/vendor/require.min.js"></script>

  <!-- 
  I put my require js config inline for simplicity
  -->
  <script type="text/javascript">
    require.config({
      baseUrl: 'js',
      shim: {
          'underscore': {
              exports: '_'
          },
          'react': {
              exports: 'React'
          }
      },
      paths: {
          jquery: 'vendor/jquery.min',
          underscore: 'vendor/underscore.min',
          react: 'vendor/react.min'
      }
    });
  </script>

  <!-- 
  I put my spec files here
  -->
  <script type="text/javascript" src="spec/a-spec.js"></script>
  <script type="text/javascript" src="spec/some-other-spec.js"></script>
</head>

<body>
</body>
</html>

Now here is an example spec file: 现在这是一个示例spec文件:

describe("Circular List Operation", function() {

    // The CircularList object needs to be loaded by RequireJs
    // before we can use it.
    var CircularList;

    // require.js loads scripts asynchronously, so we can use
    // Jasmine 2.0's async support. Basically it entails calling
    // the done function once require js finishes loading our asset.
    //
    // Here I put the require in the beforeEach function to make sure the
    // Circular list object is loaded each time.
    beforeEach(function(done) {
        require(['lib/util'], function(util) {
            CircularList = util.CircularList;
            done();
        });
    });

    it("should know if list is empty", function() {
        var list = new CircularList();
        expect(list.isEmpty()).toBe(true);
    });

    // We can also use the async feature on the it function
    // to require assets for a specific test.
    it("should know if list is not empty", function(done) {
        require(['lib/entity'], function(entity) {
            var list = new CircularList([new entity.Cat()]);
            expect(list.isEmpty()).toBe(false);
            done();
        });
    });
});

Here is a link the async support section from the Jasmine 2.0 docs: http://jasmine.github.io/2.0/introduction.html#section-Asynchronous_Support 以下是来自Jasmine 2.0文档的异步支持部分的链接: http//jasmine.github.io/2.0/introduction.html#section-Asynchronous_Support

Another option for Jasmine 2.0 standalone is creating a boot.js file and setting it up to run your tests after all of your AMD modules have been loaded. Jasmine 2.0 standalone的另一个选项是创建一个boot.js文件并将其设置为在加载所有AMD模块后运行测试。

The ideal end user case for writing tests in our case was to not have to list out all of our spec files or dependencies in once explicit list, and only have the requirement of declaring your *spec files as AMD modules with dependencies. 在我们的案例中编写测试的理想最终用户案例是不必在一次显式列表中列出所有规范文件或依赖项,并且只需要将您的* spec文件声明为具有依赖项的AMD模块。

Example ideal spec: spec/javascript/sampleController_spec.js 示例规范: spec / javascript / sampleController_spec.js

require(['app/controllers/SampleController'], function(SampleController) {
  describe('SampleController', function() {
      it('should construct an instance of a SampleController', function() {
        expect(new SampleController() instanceof SampleController).toBeTruthy();
      });
  });
});

Ideally the background behaviour of loading the dependency in and running the specs would be totally opaque to anyone coming on to the project wanting to write tests, and they won't need to do anything other than create a *spec.js file with AMD dependencies. 理想情况下,加载依赖项并运行规范的后台行为对于想要编写测试的项目的任何人来说都是完全不透明的,除了创建具有AMD依赖项的* spec.js文件之外,他们不需要做任何事情。 。

To get this all working, we created a boot file and configured Jasmine to use it ( http://jasmine.github.io/2.0/boot.html ), and added some magic to wrap around require to temporarily delay running tests until after we have our deps loaded: 为了使这一切正常工作,我们创建了一个启动文件并配置了Jasmine来使用它( http://jasmine.github.io/2.0/boot.html ),并添加了一些魔法来包裹需要暂时延迟运行测试,直到之后我们装了deps:

Our boot.js ' "Execution" section: 我们的boot.js '“执行”部分:

/**
 * ## Execution
 *
 * Replace the browser window's `onload`, ensure it's called, and then run all of the loaded specs. This includes initializing the `HtmlReporter` instance and then executing the loaded Jasmine environment. All of this will happen after all of the specs are loaded.
 */

var currentWindowOnload = window.onload;

// Stack of AMD spec definitions
var specDefinitions = [];

// Store a ref to the current require function
window.oldRequire = require;

// Shim in our Jasmine spec require helper, which will queue up all of the definitions to be loaded in later.
require = function(deps, specCallback){
  //push any module defined using require([deps], callback) onto the specDefinitions stack.
  specDefinitions.push({ 'deps' : deps, 'specCallback' : specCallback });
};

//
window.onload = function() {

  // Restore original require functionality
  window.require = oldRequire;
  // Keep a ref to Jasmine context for when we execute later
  var context = this,
      requireCalls = 0, // counter of (successful) require callbacks
      specCount = specDefinitions.length; // # of AMD specs we're expecting to load

  // func to execute the AMD callbacks for our test specs once requireJS has finished loading our deps
  function execSpecDefinitions() {
    //exec the callback of our AMD defined test spec, passing in the returned modules.
    this.specCallback.apply(context, arguments);        
    requireCalls++; // inc our counter for successful AMD callbacks.
    if(requireCalls === specCount){
      //do the normal Jamsine HTML reporter initialization
      htmlReporter.initialize.call(context);
      //execute our Jasmine Env, now that all of our dependencies are loaded and our specs are defined.
      env.execute.call(context);
    }
  }

  var specDefinition;
  // iterate through all of our AMD specs and call require with our spec execution callback
  for (var i = specDefinitions.length - 1; i >= 0; i--) {
    require(specDefinitions[i].deps, execSpecDefinitions.bind(specDefinitions[i]));
  }

  //keep original onload in case we set one in the HTML
  if (currentWindowOnload) {
    currentWindowOnload();
  }

};

We basically keep our AMD syntax specs in a stack, pop them off, require the modules, execute the callback with our assertions in it, then run Jasmine once everything is done loading in. 我们基本上将我们的AMD语法规范保存在堆栈中,弹出它们,需要模块,使用我们的断言执行回调,然后在一切都完成加载后运行Jasmine。

This set up allows us to wait until all of the AMD modules required by our individual tests are loaded, and doesn't break AMD patterns by creating globals. 这个设置允许我们等到我们各个测试所需的所有AMD模块都被加载,并且不会通过创建全局变量来破坏AMD模式。 There's a little hackery in the fact that we temporarily override require, and only load our app code using require (our `src_dir: in jasmine.yml is empty), but the overall goal here is to reduce the overhead of writing a spec. 我们暂时覆盖require,并且只使用require加载我们的应用程序代码(我们的`src_dir:jasmine.yml中为空),但是这里的总体目标是减少编写规范的开销。

you can use done in combo with before filters to test asynchronous callbacks: 你可以使用在过滤器之前组合done来测试异步回调:

  beforeEach(function(done) {
    return require(['dist/sem-campaign'], function(campaign) {
      module = campaign;
      return done();
    });
  });

This is how I do to run a jasmine spec in a html using AMD/requirejs for all my sources and specs. 这是我如何使用AMD / requirejs为我的所有源和规格在html中运行jasmine规范。

This is my index.html file that loads jasmine and then my 'unit test starter' : 这是我的index.html文件加载jasmine然后我的'单元测试启动器':

<html><head><title>unit test</title><head>
<link rel="shortcut icon" type="image/png" href="/jasmine/lib/jasmine-2.1.3/jasmine_favicon.png">
<link rel="stylesheet" href="/jasmine/lib/jasmine-2.1.3/jasmine.css">
<script src="/jasmine/lib/jasmine-2.1.3/jasmine.js"></script>
<script src="/jasmine/lib/jasmine-2.1.3/jasmine-html.js"></script>
<script src="/jasmine/lib/jasmine-2.1.3/boot.js"></script>
</head><body>
<script data-main="javascript/UnitTestStarter.js" src="javascript/require.js"></script>
</body></html>

and then my UnitTestStarter.js is something like this: 然后我的UnitTestStarter.js是这样的:

require.config({
    "paths": {
        ....
});
require(['MySpec.js'], function()
{
    jasmine.getEnv().execute();
})

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM