Your First AngularJS App Tutorial Part 2: Tools for Scaffolding, Building, and Testing

View all articles


With the many tools available to aid in developing AngularJS applications, many people have the impression that it’s an extremely complicated framework, which is not at all the case. That’s one of the main reasons I started this tutorial series.

In part one we covered the basics of the AngularJS framework and started out by writing our first application. This post is designed for beginners. If you’re a more experienced AngularJS developer, you might be more interested in demystifying directives or a story of AngularJS in use at a growing startup.

In this tutorial, we’re going to set aside the application logic layer and learn how to conduct proper AngularJS project setup, including scaffolding, dependency management, and preparing it for testing (both unit and end-to-end). We’ll do this using these AngularJS tools: Yeoman, Grunt, and Bower. Then, we’ll review the process of writing and running Jasmine tests using Karma.

Karma, Jasmine, Grunt, Bower, Yeoman… What are all these tools?

There are so many development tools to aid in scaffolding AngularJS, testing AngularJS and building an app properly.

If you work with JavaScript, it’s highly probable that you already know of at least some of these tools, even if you’re new to Angular. But to help ensure a common baseline, I’ll avoid making any assumptions. Let’s briefly review each of these technologies and what it’s useful for:

  • Karma (previously known as Testacular) is Google’s JavaScript test runner and the natural choice for testing AngularJS. In addition to allowing you to run your tests on real browsers (including phone/tablet browsers), it is also test framework agnostic; which means that you can use it in conjunction with any test framework of your choice (such as Jasmine, Mocha, or QUnit, among others).

  • Jasmine will be our test framework of choice, at least for this post. Its syntax is quite similar to that of RSpec, if you’ve ever worked with that. (If you haven’t, don’t worry; we’ll check it out in greater detail later in this tutorial.)

  • Grunt is a task runner that helps automate several repetitive tasks, such as minification, compilation (or build), testing, and setting up a preview of your AngularJS application.

  • Bower is a package manager that helps you find and install all your application dependencies, such as CSS frameworks, JavaScript libraries, and so on. It runs over git, much like Rails bundler, and avoids the need to manually download and update dependencies.

  • Yeoman is a toolset containing 3 core components: Grunt, Bower, and the scaffolding tool Yo. Yo generates boilerplate code with the help of generators (which are just scaffolding templates) and automatically configures Grunt and Bower for your project. You can find generators for almost any JavaScript framework (Angular, Backbone, Ember, etc.), but since we’re focusing here on Angular, we’re going to use the generator-angular project.

So, where do we start?

Well, the first thing we’ll need to do is install the tools we’re going to need.

If you don’t have git, node.js, and npm installed already, go ahead and install them.

Then we’ll go to the command line and run the following command to install the tools of Yeoman:

npm install -g yo grunt-cli bower

Oh, and don’t forget, we’re going to use the AngularJS generator so you’ll need to install it as well:

npm install -g generator-angular

OK, now we’re ready to…

Scaffold/generate our AngularJS application

Last time, we manually borrowed our boilerplate code from the angular-seed project. This time, we’ll let yo (in conjunction with generator-angular) do that for us.

All we need to do is create our new project folder, navigate to it and run:

yo angular

We’ll be presented with some options, such as whether or not to include Bootstrap and Compass. For now, let’s say no to Compass and yes to Bootstrap. Then, when prompted about which modules to include (resource, cookies, sanitize and route), we’ll select only angular-route.js.

Our project scaffold should now be created (it may take a minute), integrated with Karma and all pre-configured.

Note: Bear in mind that we’re restricting the modules here to the ones we used in the application we built in part one of this tutorial. When you’re doing this for your own project, it will be up to you to determine which modules you’ll need to include.

Now, since we’re going to be using Jasmine, let’s add the karma-jasmine adapter to our project:

npm install karma-jasmine --save-dev

In case we want our tests to be executed on a Chrome instance, let’s also add the karma-chrome-launcher:

npm install karma-chrome-launcher --save-dev

OK, if we did everything right, our project file tree now should now look like this:

A sample project file tree using these AngularJS tools will look like this.

Our static application code goes into the app/ directory and the test/ directory will contain (yup, you guessed it!) our tests. The files we see on the root are our project configuration files. There’s a lot to be learned about each one of them, but for now we’ll just stick with the default configuration. So let’s run our app for the first time, which we can do simply with the following command:

grunt serve

And voila! Our app should now pop up in front of us!

A little about Bower for AngularJS

Before getting into the really important part (i.e., the testing), let’s take a minute to learn a bit more about Bower. As mentioned earlier, Bower is our package manager. Adding a lib or plugin to our project can simply be done using the bower install command. For example, to include modernizr, all we need to do is the following (within our project directory, of course):

bower install modernizr

Note, though, that while this does make modernizr part of our project (it will be located in the app/bower_components directory), we are still responsible for including it in our application (or managing when it should be included) as we would need to do with any manually added lib. One way to do this would be to simply add the following <script> tag to our index.html:

<script src="bower_components/modernizr/modernizr.js"></script>

Alternatively, we can use the bower.json file to manage our dependencies. After carefully following every step until now, the bower.json file should look like this:

  "name": "F1FeederApp",
  "version": "0.0.0",
  "dependencies": {
    "angular": "1.2.15",
    "json3": "~3.2.6",
    "es5-shim": "~2.1.0",
    "jquery": "~1.11.0",
    "bootstrap": "~3.0.3",
    "angular-route": "1.2.15"
  "devDependencies": {
    "angular-mocks": "1.2.15",
    "angular-scenario": "1.2.15"

The syntax is fairly self-explanatory, but more information is available here.

We can then add any additional new dependencies that we want, and then all we need is the following command to install them:

bower install

Now let’s write some tests!

OK, now it’s time to actually pick up from where we left off in part one and write some tests for our AngularJS app.

But first, there’s a little problem we need to address: Although the developers of generator-angular based their project template on the angular-seed project (which is the official Angular boilerplate), for some reason I don’t really understand, they decided to change the app folder naming conventions (changing css to styles, js to scripts, and so on).

As a result, the app we originally wrote now has paths that are inconsistent with the scaffold we just generated. To get around this, let’s download the app code from here and work with that version from this point on (it’s mostly the exact same app we originally wrote, but with the paths updated to match the generator-angular naming).

After downloading the app, navigate to the tests/spec/controllers folder and create a file named drivers.js containing the following:

describe('Controller: driversController', function () {

  // First, we load the app's module

  // Then we create some variables we're going to use
  var driversController, scope;

  beforeEach(inject(function ($controller, $rootScope, $httpBackend) {

    // Here, we create a mock scope variable, to replace the actual $scope variable
    // the controller would take as parameter
    scope = $rootScope.$new();

    // Then we create an $httpBackend instance. I'll talk about it below.
    httpMock = $httpBackend;

    // Here, we set the httpBackend standard reponse to the URL the controller is
    // supposed to retrieve from the API
      {"MRData": {"StandingsTable": {"StandingsLists" : [{"DriverStandings":[
          "Driver": {
              "givenName": 'Sebastian',
              "familyName": 'Vettel'
          "points": "397",
          "nationality": "German",
          "Constructors": [
              {"name": "Red Bull"}
          "Driver": {
              "givenName": 'Fernando',
              "familyName": 'Alonso'
          "points": "242",
          "nationality": "Spanish",
          "Constructors": [
              {"name": "Ferrari"}
          "Driver": {
              "givenName": 'Mark',
              "familyName": 'Webber'
          "points": "199",
          "nationality": "Australian",
          "Constructors": [
              {"name": "Red Bull"}

    // Here, we actually initialize our controller, passing our new mock scope as parameter
    driversController = $controller('driversController', {
      $scope: scope

    // Then we flush the httpBackend to resolve the fake http call


  // Now, for the actual test, let's check if the driversList is actually retrieving
  //  the mock driver array
  it('should return a list with three drivers', function () {

  // Let's also make a second test checking if the drivers attributes match against
  // the expected values
  it('should retrieve the family names of the drivers', function () {


This is the test suite for our driverscontroller. It may look like a lot of code, but most of it is actually just mock data declaration. Let’s take a quick look at the really important elements:

  • The describe() method defines our test suite.
  • Each it() is a proper test spec.
  • Every beforeEach() function is executed right before each of the tests.

The most important (and potentially confusing) element here is the $httpBackend service that we instantiated on the httpMock variable. This service acts as a fake back-end and responds to our API calls on the test runs, just like our actual server would do in production. In this case, using the expectJSONP() function, we set it to intercept any JSONP requests to the given URL (the same one we use to get the info from the server) and instead, return a static list with three drivers, mimicking the real server response. This enables us to know for sure what is supposed to come back from the controller. We can therefore compare the results with those expected, using the expect() function. If they match, the test will pass.

Running the tests is simply done with the command:

grunt test

The test suite for the driver details controller (drivercontroller) should be fairly similar to the one we just saw. I recommend you try to figure it out for yourself as an exercise (or you can just take a look here, if you’re not up to it).

What about end-to-end AngularJS tests?

The Angular team recently introduced a new runner for end-to-end tests called Protractor. It uses webdriver to interact with the application running in the browser and it also uses the Jasmine testing framework by default, so the syntax will be highly consistent with that of our unit tests.

Since Protractor is a fairly new tool, though, it’s integration with the Yeoman stack and generator-angular still requires a fair amount of configuration work. With that in mind, and my intention to keep this tutorial as simple as possible, my plan is to dedicate a future post exclusively to covering end-to-end testing in AngularJS in-depth.


At this point in the tutorial series, we’ve learned how to scaffold our Angular app with yo, manage it’s dependencies with bower, and write/run some tests using karma and protractor. Bear in mind, though, that this tutorial is meant only as an introduction to these AngularJS tools and practices; we didn’t analyze any of them here in great depth.

Our goal has simply been to help get you started down this path. From here, it’s up to you to go on and learn all you can about this amazing framework and suite of tools.

Addendum: Some (important) notes from the author

After reading this tutorial, some people may ask, “Wait. Aren’t you supposed to do all this stuff before you actually start coding your app? Shouldn’t this have been part one of this tutorial?”

My short answer to that is no. As we saw in part one, you don’t actually need to know all this stuff to code your first Angular app. Rather, most of the tools we’ve discussed in this post are designed to help you optimize your development workflow and practice Test-Driven Development (TDD).

And speaking of TDD, the most basic concept of TDD is certainly a sound one; namely, write your tests before you write your code. Some people, though, take that concept way too far. TDD is a development practice, not a learning method. Accordingly, writing your tests before writing your code does make a lot of sense, whereas learning how to write your tests before learning how to code does not.

I personally think this is the main reason why the official Angular tutorials may feel so convoluted and can be nearly impossible to follow for people with no previous front-end MVC/TDD experience. That is one of the main reasons why I started this tutorial series.

My personal advice for those learning to navigate the AngularJS world is: Don’t be too hard on yourself. You don’t need to learn everything all at once (despite people telling you otherwise!). Depending on your prior experience with other front-end/testing frameworks, AngularJS can be pretty hard to understand initially. So learn all you need to learn until you are able to write your own simple apps and then, once you’re comfortable with the basics of the framework, you can concern yourself with selecting and applying the long-term development practices that work best for you.

Of course, that is my humble opinion and not everyone will agree with that approach (and the Angular dev team may send a hired killer after me once I publish this), but that’s my vision and I’m pretty sure that are lots of people out there who will agree with me.

About the author

Raoni Boaventura, Brazil
member since March 9, 2012
Raoni is an experienced software developer and who has contributed to a wealth of projects using Ruby on Rails, JavaScript, and PHP on top of many other programming languages and frameworks. He is an excellent problem solver, and a great communicator as both a team member and a team lead. [click to continue...]
Hiring? Meet the Top 10 Freelance AngularJS Developers for Hire in October 2016


Majid Lotfi
Thanks Raoni, You are the best.
Are you kidding people? This is by far the easiest-to-follow angular introduction I have ever found. The reason I loved the articles so much was exactly how incremental it was. Great job Mr. Boaventura.
This is one of the best resources I've found out there. I couldn't run the part one of the tutorial but after reading this I managed to integrate it with Yeoman. I've learnt so much thanks to you. God bless you!
Great article again. Thanks Raoni.
David Land
Guergana Tzatchkova
I can't get passt the "yo angular" command. I get " 'yo' is not recognized as an internal or external command, operable program or batch file."
Raoni Boaventura
Well, apparently, you didn't install yo. Did you run "npm install -g yo grunt-cli bower"? If yes, what happens when you do it?
Hi, Have you guys tried running > grunt build I keep on getting an Warning Warning: dist/views/driver.html Parse Error: <- Back to drivers list</a> Is that something I have to worry about? Thank you,
I am sathish . I am a Product Tester. Needed to start with unit testing and I am very new to it . I just blindly followed these steps given. Does Anybody Successfully installed "Yo" .I got few errors . Then i followed the steps give in some link: use as below: ---------------------------- cd ~ git clone Edit the package.json.. nano yo/package.json .. to remove these lines.. "scripts": { "test": "grunt", "postinstall": "node ./scripts/doctor", "postupdate": "node ./scripts/doctor" }, Then, install it using npm.. cd yo npm install -g If you want to clean up you can remove the Yeoman repo.. cd .. rm -R yo/ ------------------------------------- Now Some other problems i end up with . Somebody please tell me who got fully working with these steps . Many Thanks Sathish
Bartek Gluszak
Very good introduction. I have one remark - the debugger would be very helpful to write a test. Producing tests in a manner presented above is difficult because the test in launched automatically in the browser and I cannot debug the code in the browser, because the browser closes immediately.
Awesome Intro. Getting this error while unit-testing driverController., any Ideas how to fix this ? Error: Unexpected request: JSONP Expected JSONP driver test-case: var driverController, scope, httpMock; var driverDetailsUrl = ''; var mockDriverData = //EXCLUDED in this comment, but this is JSON from the above URL. beforeEach(inject(function ($controller, $rootScope, $httpBackend, $routeParams) { scope = $rootScope.$new(); routeParams = $routeParams; = 'vettel'; httpMock = $httpBackend; httpMock.expectJSONP(driverDetailsUrl).respond(mockDriverData); // Here, we actually initialize our controller, passing our new mock scope as parameter driverController = $controller('driverController', { $scope: scope, $routeParams: routeParams }); httpMock.flush(); })); it('should return driver details ', function () { expect(scope.driver.Driver.driverId).toEqual('vettel'); });
Satish, If you have node installed correctly, then this command should install Yeoman, along with bower & grunt. npm install -g yo grunt-cli bower. if you are using a mac/linux, you may need to add sudo before the command depending on access. sudo npm install -g yo grunt-cli bower
Thanks for the tutorial, but the test for DriverController does not work for me. I'm always getting this: Error: Unexpected request: JSONP No more request expected Do you know what to do?
Saransh Mohapatra
Thanks for the awesome tutorial. Its are a real help in getting started with angular.js I wanted to know one more thing though...How would you put this angular code in production. I have seen at some places they say to use grunt build and it builds a server.js too in the dist directory which can be run. But what if I already have an server running with REST API and just want to add the angular part to handle those API's.
Golak Jena
Great Post.
Arvind Chavar
Great article. Pretty good resource to get handle on Jasmine and test file in tutorial.Should be quick 5-10 minute read
Rony Mattar
Thanks for the post. I want to use it in a PHP project, how can I do that? And after I deploy the site online what steps I have to do to make it run?
I concur. Not just his writing style, but his stated approach is clearly superior
No words to thank you! =)
Neil Ryder
Luciano Mammino
Great article. I especially loved the addendum with your considerations about testing, TDD and learning. With a bit of experience about these themes I share your opinion on them, but at the beginning I was very tempted to mix learning a new technology (eg. Angular) and TDD and it was really a bad idea.
is there way to add https server with Yeoman ?
Jeffrey Wan
In this code, what is driversList and where is it coming from? it('should return a list with three drivers', function () { expect(scope.driversList.length).toBe(3); });
Angelo Rigo
Another great tutorial! thank´s a lot your tutorials are helping me me a lot, please keep calm and just keep posting
David Emukpere
Thanks a lot for this awesome tutorial
karma appears not working with Angular and RequireJS applications.
sriram angajala
Nice tutorial and have given over view of the components
Ideal tutorial when from scratch like me.
Anyone has a good tutorial on how to compile and minify angularjs apps?
You have to look at the driversController code. This is where the driverList is added to the $scope
Tal Avissar
Great Job
Great Tutorial Raoni. I cloned your git and ran : grunt test. I get this failure "Chrome have not captured in 60000 ms, killing:" Any idea ? ( It launches chrome and seems to go in some kind of loop). I tried Safari too. Same behavior.
Renato Back
After some node updates, you have to include generator-karma at the install command line, so the full version is: npm install -g yo grunt-cli bower generator-karma generator-angular
comments powered by Disqus
The #1 Blog for Engineers
Get the latest content first.
No spam. Just great engineering and design posts.
The #1 Blog for Engineers
Get the latest content first.
Thank you for subscribing!
You can edit your subscription preferences here.
Trending articles
Relevant technologies
About the author
Raoni Boaventura
JavaScript Developer
Raoni is an experienced software developer and who has contributed to a wealth of projects using Ruby on Rails, JavaScript, and PHP on top of many other programming languages and frameworks. He is an excellent problem solver, and a great communicator as both a team member and a team lead.