Получение requirejs для работы с Jasmine
сначала я хочу сказать, что я новичок в RequireJS и даже новее Жасмин.
у меня возникли некоторые проблемы со Спекраннером и требуют JS. Я следил за учебниками УЗИ Килона и Бена надела (наряду с некоторыми другими), и они помогли некоторым, но у меня все еще есть некоторые проблемы.
кажется, что, если есть ошибка, которая выбрасывается в тесте (я могу думать об одном, в частности, об ошибке типа), будет отображаться HTML-код Spec runner. Это говорит мне, что я есть некоторые проблемы в JavaScript. Однако после того, как я исправлю эту ошибку, HTML больше не отображается. Я не могу заставить тестовый бегун отображаться вообще. Может кто-то найти что-то не так с моим кодом, что может вызвать эту проблему?
вот мой структура каталогов:
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
здесь SpecRunner (index) 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>
здесь 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'
}
}
});
вот спецификация:
require(["../lib/jasmine/src/notepad"], function (notepad) {
describe("returns titles", function() {
expect(notepad.noteTitles()).toEqual("");
});
});
Блокнот источник:
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;
}
};
});
и источник Примечания (JIC):
define(function (){
var note = function(title, content) {
this.title = title;
this.content = content;
};
return note;
});
я убедился, что, насколько приложение касается, пути являются правильными. Как только я получу эту работу, я могу играть с настройкой этих путей, чтобы это было не так противно.
5 ответов:
мне удалось заставить это работать с некоторыми проб и ошибок. Основная проблема заключалась в том, что при написании спецификаций это не требование, которое вы хотите создать, вы хотите использовать define:
Оригинал:
require(["/lib/jasmine/src/notepad"], function (notepad) { describe("returns titles", function() { expect(notepad.noteTitles()).toEqual("pick up the kids get milk"); }); });
работает:
define(["lib/jasmine/src/notepad"], function (notepad) { describe("returns titles", function () { it("something", function() { expect(notepad.noteTitles()).toEqual("pick up the kids get milk "); }); }); });
после проведения некоторых исследований стало ясно, что при использовании RequireJS все, что вы хотите использовать require (), должно быть обернуто в define (теперь кажется очевидным, я думаю). Вы можете видеть, что, в спекраннере.js-файл, require используется при выполнении тестов (поэтому необходимо "определить" спецификации.
другая проблема заключается в том, что при создании спецификаций необходимо описывать() и it () (а не только описывать, как в опубликованном примере).
Оригинал:
describe("returns titles", function() { expect(notepad.noteTitles()).toEqual("pick up the kids get milk"); });
работает:
describe("returns titles", function () { it("something", function() { expect(notepad.noteTitles()).toEqual("pick up the kids get milk "); }); });
Я также изменил место, где существует тестовый бегун, но это был рефакторинг и не изменился итоги испытаний.
опять же, вот файлы и изменены:
Примечание.js: осталась
Блокнот.js: осталась
.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:
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:
define(["lib/jasmine/src/notepad"], function (notepad) { describe("returns titles", function () { it("something", function() { expect(notepad.noteTitles()).toEqual("pick up the kids get milk"); }); }); });
просто добавив это в качестве альтернативного ответа для людей, которые вы используете Jasmine 2.0 standalone. Я считаю, что это может работать и для Jasmine 1.3, но асинхронный синтаксис отличается и выглядит уродливо.
вот мой модифицированный Спекраннер.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>
теперь вот пример файла спецификации:
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(); }); }); });
вот ссылка на раздел поддержки асинхронности из документов Jasmine 2.0: http://jasmine.github.io/2.0/introduction.html#section-Asynchronous_Support
другой вариант для Jasmine 2.0 standalone-это создание загрузки.JS-файл и настройка его для запуска тестов после загрузки всех ваших модулей AMD.
идеальным случаем конечного пользователя для написания тестов в нашем случае было не перечислять все наши файлы спецификаций или зависимости в одном явном списке, а только требовать объявления ваших файлов *spec в качестве модулей AMD с зависимостями.
пример идеальной спецификации: 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(); }); }); });
В идеале фоновое поведение загрузки зависимости и запуска спецификаций будет полностью непрозрачным для всех, кто приходит в проект, желая написать тесты, и им не нужно будет делать ничего, кроме создания *spec.JS-файл с зависимостями AMD.
чтобы все это работало, мы создали загрузочный файл и настроили Jasmine для его использования (http://jasmine.github.io/2.0/boot.html), и добавил некоторую магию, чтобы обернуть вокруг требуют временно отложить запуск тестов до тех пор, пока мы не загрузим наши deps:
наши 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(); } };
мы в основном сохраняем наши спецификации синтаксиса AMD в стеке, вытаскиваем их, требуем модули, выполняем обратный вызов с нашими утверждениями в нем, а затем запускаем Jasmine, как только все будет загружено.
Эта настройка позволяет нам чтобы дождаться загрузки всех модулей AMD, необходимых для наших индивидуальных тестов, и не нарушать шаблоны AMD, создавая глобалы. Есть немного хакерства в том, что мы временно переопределяем require, и только загружаем наш код приложения с помощью require (our
`src_dir:
на Жасмин.в формате YML пусто), но общая цель здесь состоит в том, чтобы уменьшить накладные расходы на написание спецификации.
можно использовать
done
в комбинации с Перед фильтрами для тестирования асинхронных обратных вызовов:beforeEach(function(done) { return require(['dist/sem-campaign'], function(campaign) { module = campaign; return done(); }); });
вот как я запускаю спецификацию jasmine в html с использованием AMD / requirejs для всех моих источников и спецификаций.
Это мой индекс.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>
а потом мой UnitTestStarter.JS-это что-то вроде этого:
require.config({ "paths": { .... }); require(['MySpec.js'], function() { jasmine.getEnv().execute(); })