Basically, I'm quite experienced with Mocha (written thousands of unit tests), and I'm quite new to AngularJS (written just my first project).
Now I am wondering how I might unit test all the AngularJS stuff using Mocha.
I know that Mocha runs in the browser, and I have already done this. But how do I structure and setup things?
I guess I need to:
Load AngularJS
Load Mocha
Load my tests
Within each of the tests, I need to load a controller, a service, ... to test. How do I do that? I am not using require.js or something like that, the files are just script files with basically the following content:
angular.controller('fooController', [ '$scope', function ($scope) {
// ...
}]);
How do I reference and instantiate that controller within a test?
The same holds true for services, directives, ...
Do I need to create mocks for $scope, $http & co. for myself, or is there some help?
Please note that I am aware that there is the Karma test runner (formerly known as Testacular), but I do not want to switch my test runner completely.
One way of doing that is to use Angular $injector in your tests:
myModule_test.js
suite('myModule', function(){
setup(function(){
var app = angular.module('myModule', []);
var injector = angular.injector(['myModule', 'ng']);
var service = injector.get('myService');
});
suite('myService', function(){
test('should return correct value', function(){
// perform test with an instance of service here
});
});
});
your html should look similar to this:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>myModule tests</title>
<link rel="stylesheet" media="all" href="vendor/mocha.css">
</head>
<body>
<div id="mocha"><p>Index</p></div>
<div id="messages"></div>
<div id="fixtures"></div>
<script src="vendor/mocha.js"></script>
<script src="vendor/chai.js"></script>
<script src="angular.min.js"></script>
<script src="myModule.js"></script>
<script>mocha.setup('tdd')</script>
<script src="myModule_test.js"></script>
<script>mocha.run();</script>
</body>
</html>
If you're creating an angular service that doesn't have any dependencies and isn't necessarily angular specific, you can write your module in an angular-agnostic way, then either write a separate small angular wrapper for it, or test for the presence of angular, and conditionally create a service for it.
Here's an example of an approach that I use to create modules that can be used both in angular, the browser, and node modules, including for mocha tests:
(function(global) {
//define your reusable component
var Cheeseburger = {};
if (typeof angular != 'undefined') {
angular.module('Cheeseburger', [])
.value('Cheeseburger', Cheeseburger);
}
//node module
else if (typeof module != 'undefined' && module.exports) {
module.exports = Cheeseburger
}
//perhaps you'd like to use this with a namespace in the browser
else if (global.YourAppNamespace) {
global.YourAppNamespace.Cheeseburger = Cheeseburger
}
//or maybe you just want it to be global
else {
global.Cheeseburger = Cheeseburger
}
})(this);
Related
I am curious what the best way is to implement a service layer using Ember.js.
A can not really find any suggestions about it in the Ember Guide.
For example I have some validation code that I could use in more than one controllers, and I don't want to copy&paste them into all of them.
Would you use plain javascript objects with methods, that implement the service logic, or is there a more ember-like way to do this?
Thanks.
There is built in support for service layer in Emberjs even not documented now. You can use service layer based on dependency injection.
See example below. Also there is public repository on GitHub with example
index.html
<!doctype html>
<html lang="en" data-framework="emberjs">
<head>
<meta charset="utf-8">
<title>ember_js_di_example</title>
<script src="./bower_components/jquery/dist/jquery.js"></script>
<script src="./bower_components/handlebars/handlebars.js"></script>
<script src="./bower_components/ember/ember.js"></script>
<script src="./index.js"></script>
</head>
<body>
<script type="text/x-handlebars" id="Home">
<div>
<div>My name is <b>{{me.name}}</b></div>
<div>My wife name is <b>{{me.wife.name}}</b></div>
<div>My wife name is <b>{{wife.name}}</b> (based on controller injection)</div>
<div>My children name is <b>{{me.children.name}}</b></div>
</div>
</script>
<script type="text/x-handlebars">
{{ outlet }}
</script>
index.js
var Services = Ember.Namespace.create();
// service class one
Services.Me = Ember.Object.extend({
name: function () {
return 'Jan'
}.property()
})
// service class two
Services.Wife = Ember.Object.extend({
name: function () {
return 'Sarka'
}.property()
})
// service class three
Services.Children = Ember.Object.extend({
name: function () {
return 'Ondra'
}.property()
})
App = Ember.Application.create({
LOG_TRANSITIONS: true,
ready: function () {
// Register service class - instance will be created on demand
this.register('service:me', Services.Me);
this.register('service:wife', Services.Wife);
// Register already instantiated service
this.register('service:children', Services.Children.create(), {instantiate: false});
// inject service to all routes, controllers, service will be accessible as me property
this.inject('route', 'me', 'service:me');
this.inject('controller', 'me', 'service:me');
// inject wife and children to me, will be accessible as wife and children property
this.inject('service:me', 'wife', 'service:wife');
this.inject('service:me', 'children', 'service:children');
// inject wife only to application controller, will be accessible as wife property on home controller
this.inject('controller:Home', 'wife', 'service:wife')
}
});
App.Router.map(function () {
this.resource('Home', {path: '/home'});
});
I think at the moment the best way to reuse controller code would be to use inheritence... that is, if you want common code in multiple controllers, then create a sort of abstract controller with the duplicate logic in it and then extend that for your other controllers. That's what I've done in the past and it's worked quite well for me.
I've had a similar issue with routers, though. You could always use a mixin as well.
Example:
App.MyAbstractController = Em.Controller.extend({
commonMethod: function() {
// something common to all controllers
}
})
App.ActualControllerOne = App.MyAbstractController.extend();
App.ActualControllerTwo = App.MyAbstractController.extend();
I prefer unit testing in the browser because I enjoy the legible formatting of testing frameworks like mocha.
Is there an easy way run add-on sdk unit tests in the browser?
Edit: I'm not looking to do regression testing. With mocha, for example, I can create an HTML page like this:
<head>
<title>Tests</title>
<link rel="stylesheet" href="mocha.css" />
</head>
<body>
<script src="mocha.js"></script>
<script src="chai.js"></script>
<script src="main-content.js"></script>
<script>mocha.setup('bdd')</script>
<script src="main-content-test.js"></script>
<script>
mocha.run();
</script>
</body>
The output looks like the picture I linked to above. This is what I mean by unit testing in the browser.
Edit 2: I'm trying to unit test my modules (using require("sdk/test"), not my content scripts.
There is not a prebuilt way to do this, it would require a little jerry-rigging.
All that you need in addition to your page is a page-mod like this:
require('sdk/page-mod').PageMod({
include: require('sdk/self').data.url('testpage.html'),
contentScript: 'unsafeWindow.addon={on: self.port.on, emit: self.port.emit}',
contentScriptWhen: 'start',
onAttach: function(worker) {
worker.port.on('test-1', function() {
worker.port.emit('test-1', {})
});
// ...
}
});
which would listen for test events, perform the test, and emit the results back. Then your test page test code would listen for the test results like so:
function test() {
addon.on('test-1', function(results) {
// loop through results and pass/fail
});
addon.emit('test-1');
}
I have some JavaScript that I'm testing with Jasmine. I want to run the tests in the browser window when a user presses "run tests". With Jasmine 1.3, I have successfully set that up as shown in this JSFiddle with this code:
run tests
<script type="text/javascript">
window.jasmineEnv = (function () {
var jasmineEnv = jasmine.getEnv();
jasmineEnv.updateInterval = 1000;
var htmlReporter = new jasmine.HtmlReporter();
jasmineEnv.addReporter(htmlReporter);
jasmineEnv.specFilter = function (spec) {
return htmlReporter.specFilter(spec);
};
return jasmineEnv;
})();
</script>
Jasmine 2.0 offers some new capabilities that I really need. However, I cannot figure out how to get it setup such that the tests run when someone clicks a "run tests" button. I'm using the new boot.js file. However, I'm not having any luck. Can someone please help me migrate that sample from Jasmine 1.3 to Jasmine 2.0?
Thank you
Test cases execution is triggered by below snipped in file boot.js:
window.onload = function() {
if (currentWindowOnload) {
currentWindowOnload();
}
htmlReporter.initialize();
env.execute();
};
Either you can modify this implementation in boot.js file itself to execute under a function call or you can write your custom boot code inspired from actual boot.js.
Can't post this as a comment yet!
Jasmine 2.0 in jsfiddle http://jsfiddle.net/88Xa6/4/ As mentioned by #user3037143 initialization is handled at boot.js.
Ensure the library files are in place:
<script type='text/javascript' src="/libs/jasmine/2.0.0/jasmine.js"></script>
<script type='text/javascript' src="/libs/jasmine/2.0.0/jasmine-html.js"></script>
<link rel="stylesheet" type="text/css" href="/libs/jasmine/2.0.0/jasmine.css">
<!-- Add any custom reporters (Console / Junit / etc) here.
Ensure necessary initialization triggers are set in
boot when adding more reporters. -->
<script type='text/javascript' src="/libs/jasmine/2.0.0/boot.js"></script>
You can either choose to include the spec or have them defined inline:
<script src="spec.js"></script>
or
<script type='text/javascript'>
describe("My Suite", function() {
it("Should be true", function() {
expect(1).toBe(1);
});
});
</script>
I want to test my angular app with Yeoman which use Mocha with Phantom and Chai for assertion.
But when i run any sample test case the test case do not run properly it shows PhantomJs timed out due to missing Mocha run() call.Non angular Cases are working fine in test case.
<!doctype html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Mocha Spec Runner</title>
<link rel="stylesheet" href="lib/mocha/mocha.css">
</head>
<body>
<div id="mocha"></div>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.5/angular.min.js"></script>
<script src="lib/mocha/mocha.js"></script>
<script>mocha.setup('bdd')</script>
<script src="lib/chai.js"></script>
<script>
expect = chai.expect;
assert = chai.assert;
</script>
<script>
function addSum(num1, num2) {
return num1 + num2;
}
</script>
<script>
(function() {
describe('Give it some context', function() {
it('should simulate promise', inject(function ($q, $rootScope) {
assert.notStrictEqual(3, '3', 'no coercion for strict equality');
/* var deferred = $q.defer();
var promise = deferred.promise;
var resolvedValue;
promise.then(function(value) { resolvedValue = value; });
expect(resolvedValue).to.be.undefined;
// Simulate resolving of promise
deferred.resolve(123);
// Note that the 'then' function does not get called synchronously.
// This is because we want the promise API to always be async, whether or not
// it got called synchronously or asynchronously.
expect(resolvedValue).to.be.undefined
// Propagate promise resolution to 'then' functions using $apply().
$rootScope.$apply();
expect(resolvedValue).to.equal(123);*/
}));
});
})();
</script>
<!-- trigger the mocha runner -->
<script src="runner/mocha.js"></script>
</body>
</html>
Have you tried using protractor? It has been developed specifically for testing end to end angularjs apps (by the angularjs team). https://github.com/angular/protractor
It has it's own runner, which you install with:
npm install protractor -g
and then the runner is executed with:
protractor /configfile.cfg
No need for an HTML page to run the tests.
The config file is quite simple (you can see the options in the source code).
With that, you'll have the spec defined as:
// myTest.js
describe('angularjs homepage', function() {
it('should greet the named user', function() {
browser.get('http://www.angularjs.org');
element(by.model('yourName')).sendKeys('Julie');
var greeting = element(by.binding('yourName'));
expect(greeting.getText()).toEqual('Hello Julie!');
});
});
I was trying to make a Backbone Application with Django at its backend. I was following a Backbone tutorial. I used the following code:
Code
<!doctype html>
<html lang = "en">
<meta charset = "utf-8">
<title>IstreetApp</title>
<link rel="stylesheet" href = "https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/2.3.1/css/bootstrap.min.css">
</head>
<body>
<div class="container">
<h1>Book Manager</h1>
<hr />
<div class="page"></div>
</div>
<script type = "text/template" id = "booklist.template">
</script>
<script src = "https://cdnjs.cloudflare.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src = "https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.4.4/underscore-min.js"></script>
<script src = "https://cdnjs.cloudflare.com/ajax/libs/backbone.js/0.9.10/backbone-min.js"></script>
<script>
$.ajaxPrefilter( function( options, originalOptions, jqXHR ) {
options.url = 'http://backbonejs-beginner.herokuapp.com' + options.url;
});
var Books = Backbone.Collection.extend({
url: '/books'
});
var BookList = Backbone.View.extend({
el: '.page',
render: function () {
var that = this;
var books = new Books();
books.fetch({
success: function(books) {
var template = _.template($('#booklist.template').html(), {books: books.models});
that.$el.html(template);
}
})
}
});
var Router = Backbone.Router.extend({
routes: {
'': 'home'
}
})
var bookList = new BookList();
var router = new Router();
router.on('route:home', function () {
bookList.render();
});
Backbone.history.start();
</script>
</body>
</html>
Since the collection is not defined, the success code doesn't execute. I suppose the collection data should come from the server through Django but I am not sure how and in what form. Kindly help. I am pretty much new to backbone and Django.
When you call fetch on your collection, it makes an AJAX request to:
http://backbonejs-beginner.herokuapp.com/books
However, there is no API set up there. Either one of two things needs to happen:
1) you need to modify your code to point to a different URL, one that does have an existing API (perhaps whatever tutorial you are using has such an API)
2) you need to create such an API yourself on yoursever.com (and then make your Backbone code point to that API's URL instead)
Without a server to support it, operations like save and fetch and such in Backbone simply cannot function.
As a side note, Django is a web site framework. While you can use it to create server-side APIs, that's not really Django's focus. Because of this, several good third party libraries exist for doing RESTful APIs (ie. the kind that Backbone likes) in Django; personally I'd recommend either Django REST Framework (I use it and it works great) or TastyPie (never used it, but it's very popular).
When using a backbone collection you need to return a json array of objects from your api url (http://backbonejs-beginner.herokuapp.com/books)
Example
{[{"name":"bookname", "publisher": "penguin"}, {"name":"bookname", "publisher":"penguin"}]}
You'll also want a model for your collection. A model would look like this
Example:
var Book = Backbone.Model.extend({
defaults: {
"name": "",
"publisher": ""
}
});
The way that the collection works is by parsing the json array and turning each object in the array in to a model that you specific (in this instant an individual book with values for the name and publisher).
When you make a .fetch() on your model you are making a GET request, so make sure that your http://backbonejs-beginner.herokuapp.com/books url is prepared to receive GET requests and respond with the json data in the format I specified up top.