Получение требований к работе с Жасмин
Прежде всего хочу сказать, что я новичок в RequireJS и даже новичок в Jasmine.
У меня возникают некоторые проблемы с SpecRunner и требуются JS. Я слежу за учебниками Узи Килона и Бена Наделя (наряду с некоторыми другими), и они помогли некоторым, но у меня все еще есть некоторые проблемы.
Похоже, что если в тесте возникла ошибка (я могу вспомнить, в частности, ошибку типа), отобразится spec runner html. Это говорит мне, что у меня есть некоторые проблемы в 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;
});
Я убедился, что в отношении приложения правильные пути. Как только я получу эту работу, я могу играть с настройкой этих путей, чтобы она не была такой яркой.
Ответы
Ответ 1
Мне удалось заставить это работать с некоторыми проб и ошибок. Основная проблема заключалась в том, что когда вы пишете спецификации, это не требуется, что вы хотите создать, вы хотите использовать 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() использовать, должно быть завернуто в определение (кажется, теперь я думаю). Вы можете видеть, что в файле specRunner.js при использовании тестов используется требование (поэтому необходимо "определить" спецификации.
Другая проблема заключается в том, что при создании спецификаций необходимо описать описание() AND the(), а не только описание, как в опубликованном примере.
Оригинал:
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 ");
});
});
Я также изменил место, где существует тестовый бегун, но это был рефактор и не изменил результат тестов.
Опять же, вот файлы и измененные:
note.js: остался тем же самым
notepad.js: остался тем же
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:
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");
});
});
});
Ответ 2
Просто добавьте это как альтернативный ответ для людей, которые используют Jasmine 2.0 отдельно. Я считаю, что это может работать и для Jasmine 1.3, но асинхронный синтаксис отличается и отвратительным.
Вот мой модифицированный файл 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>
Теперь вот пример файла 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 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();
});
});
});
Вот ссылка секция поддержки async из документов Jasmine 2.0: http://jasmine.github.io/2.0/introduction.html#section-Asynchronous_Support
Ответ 3
Другой вариант автономного Jasmine 2.0 - создать файл boot.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 `onload`, ensure it 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 (наш `src_dir:
в jasmine.yml пуст), но общая цель здесь - уменьшить накладные расходы на разработку спецификации.
Ответ 4
вы можете использовать done
в комбо с фильтрами перед проверкой асинхронных обратных вызовов:
beforeEach(function(done) {
return require(['dist/sem-campaign'], function(campaign) {
module = campaign;
return done();
});
});
Ответ 5
Вот как я делаю, чтобы запустить спецификацию jasmine в html, используя AMD/requirejs для всех моих источников и спецификаций.
Это мой файл index.html, который загружает жасмин, а затем мой "unit test стартер":
<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();
})