Sign In Start Free Trial
Account

Add to playlist

Create a Playlist

Modal Close icon
You need to login to use this feature.
  • Book Overview & Buying Learning UnderscoreJS
  • Table Of Contents Toc
Learning UnderscoreJS

Learning UnderscoreJS

By : Pop
4.6 (5)
close
close
Learning UnderscoreJS

Learning UnderscoreJS

4.6 (5)
By: Pop

Overview of this book

Underscore.js is one of the most popular modern JavaScript libraries used for functional programming. It can be used as a base for building complex JavaScript applications in a sustainable manner and for building other JavaScript libraries. It embraces functional programming principles but is not opinionated and can be used with imperative, object-oriented, functional, or other programming styles. This book explores how to use Underscore.js to power your code and understand modern JavaScript development concepts while applying a lightweight and efficient workflow to build applications. The book starts with an incremental Underscore.js introduction by exploring key JavaScript concepts. You will then explore the basic features of Underscore.js in action and establish a lightweight development workflow that allows the provided examples to be guided by tests. The book then covers the functionality of Underscore.js with in-depth examples and explanations for understanding and applying the Underscore.js API. You'll also learn how to use Underscore.js as a base for your own modules and libraries within an object-oriented or functional programming style, and will be able to explore Underscore.js use cases in different environments. Eventually, you'll learn about libraries that are closely related with Underscore.js, how to share code between client and server, and how to prepare for the upcoming JavaScript standard ECMAScript 6.
Table of Contents (9 chapters)
close
close
8
Index

Testing JavaScript code with Jasmine

Maintaining complex JavaScript codebases is challenging due to the dynamic nature of the language and the lack of built-in module support (up until ES6). Applying unit testing practices helps alleviate these issues and JavaScript as a language benefits from a large number of unit testing frameworks, libraries, and tools.

We will now add tests for the previous example found in the underscore.map.reduce-with-local-dependencies folder from the source code for this chapter. To implement these tests we will use Jasmine, a popular test framework.

Jasmine is a behavior-driven development (BDD) framework that contains extensive built-in functionality to define and execute tests for JavaScript code. A BDD framework differs from other test frameworks such as QUnit by defining tests as a desired behavior, where the test outcome is specified first followed by the actual test assertion. Jasmine uses a describe/it syntax to define test specifications, while other BDD frameworks use a given/when/then syntax. Using BDD tests produces output similar to a specification document and these types of tests are usually called specs. Other advantages of using Jasmine are that it does not rely on any other library, it has a rich functionality for defining tests and tests assertions, and it has great documentation available at http://jasmine.github.io.

Jasmine introduction

A typical Jasmine test that asserts the result of a trivial JavaScript operation will have the following code (with the Jasmine specific functions highlighted):

describe("A basic JavaScript add operation", function() {
  it("should be correct", function() {
    var result = 1 + 1;
    expect(result).toEqual(2);
  });
});

When running the test, its output should read A basic JavaScript add operation should be correct, forming a meaningful statement about the value delivered by the code being tested. The describe call is a Jasmine global function that will group one or more test specifications that are defined by the it function (which is also another Jasmine global function). Both functions have a test-related description as the first argument. The second argument is a function that defines the test suite in its body with the test specification (or the spec) defined in the it function. Test assertions use the Jasmine global function expect, which is chained with a helper function called matcher that will facilitate the test result evaluation.

There are a couple of built-in matcher functions available, such as toBe(), which checks whether the test assertion object and the expected object are the same; toEqual(), which checks whether the two objects are equivalent; and toBeDefined(), which checks whether the test assertion object is not undefined. You can also define your own custom matchers for more complex expectation checks. Jasmine allows you to set up and tear down data before and after a spec is executed through the global functions beforeEach() and afterEach().

Adding tests using the default Jasmine infrastructure

Before creating the tests, we need to modify the awardAgeCalculator.js file that contains the code under test (or SUTsystem under test) and ensure it is testable. A SUT is testable if it allows swapping out its dependencies, if it can be tested in isolation and does not depend on a shared or global application state.

For our example, we need to test that the two global accessible (or public) functions of the awardAgeCalculator object (the SUT) are producing the expected results when executed against specific data sets. Currently, we cannot easily swap out the default array of people used in the example and we need to change it by making the getPeople() function public and changing the rest of the functions to accept an input array as highlighted in the next code snippet:

var awardAgeCalculator = (function() {
  "use strict";

  var getPeople = function() {
    return [{
      name: "Herta Muller",
      birthYear: 1953,
      awardYear: 2009
    }, {
      ...
    }, {
      name: "Patrick Modiano",
      birthYear: 1945,
      awardYear: 2014
    }];
  };

  return {
    getPeople: getPeople,
    calculateAwardAgeForPeople: function(people) {
      return _.map(people, function(person) {
        return {
          name: person.name,
          awardAge: person.awardYear - person.birthYear
        };
      });
    },
    getAverageAwardAgeForPeople: function(people) {
      var peopleWithAwardAge = this.calculateAwardAgeForPeople(people);
      return _.reduce(peopleWithAwardAge, function(memo, person) {
        return memo + person.awardAge;
      }, 0) / peopleWithAwardAge.length;
    }
  };
}());

We also exposed the getPeople() function to the global scope for convenient access to the default data set. By making these changes, we have created a testable SUT where we can alter the input data for the two functions we plan to test. We can now write the tests by creating a spec\awardAgeCalculatorSpec.js file and using the following code:

describe("Given awardAgeCalculator", function() {
  describe(
    "when calling calculateAwardAgeForPeople()",
    function() {
      var people;
      var peopleWithAwardAge;
      beforeEach(function() {
        people = awardAgeCalculator.getPeople();
        peopleWithAwardAge = awardAgeCalculator.calculateAwardAgeForPeople(people);
      });
      it(
        "then the award age for the first person should be correct",
        function() {
          expect(peopleWithAwardAge[0].name).toEqual("Herta Muller");
          expect(peopleWithAwardAge[0].awardAge).toEqual(56);
        });
      it(
        "then the award age of the last person should be correct",
        function() {
          expect(peopleWithAwardAge[peopleWithAwardAge.length - 1].name).toEqual("Patrick Modiano");
          expect(peopleWithAwardAge[peopleWithAwardAge.length - 1].awardAge).toEqual(69);
        });
    });
  describe(
    "when calling getAverageAwardAgeForPeople()",
    function() {
      var people;
      var aveargeAwardAge;
      beforeEach(function() {
        people = awardAgeCalculator.getPeople();
        aveargeAwardAge = awardAgeCalculator.getAverageAwardAgeForPeople(people);
      });
      it("then the average award age should be correct", function() {
        expect(Math.floor(aveargeAwardAge)).toEqual(69);
      });
    });
});

The tests are defined within two nested describe functions and we used a beforeEach function to avoid code duplication when exercising the SUT. The expectations for the first set of tests are verifying that the person name and the award age are correct.

To execute these tests, we need to add Jasmine support to our example. We will use Bower to install Jasmine as a development package (a package that can be omitted when the current project is deployed to a target environment) through the following command:

bower install jasmine#2.3.4 --save-dev

We will change the default SpecRunner.html file provided with the standalone Jasmine distribution at http://bit.ly/1EhdgHT to reference the files from the Bower package together with the SUT file (the code file) and the test file as highlighted in the following code:

<!DOCTYPE HTML>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <title>Jasmine Spec Runner v2.3.4</title>
  <link rel="shortcut icon" type="image/png" href="bower_components/jasmine/images/jasmine_favicon.png">
  <link rel="stylesheet" type="text/css" href="bower_components/jasmine/lib/jasmine-core/jasmine.css">
  <script type="text/javascript" src="bower_components/jasmine/lib/jasmine-core/jasmine.js"></script>
  <script type="text/javascript" src="bower_components/jasmine/lib/jasmine-core/jasmine-html.js"></script>
  <script type="text/javascript" src="bower_components/jasmine/lib/jasmine-core/boot.js"></script>

  <!-- include source files here... -->
  <script src="bower_components/underscore/underscore.js"></script>
  <script type="text/javascript" src="awardAgeCalculator.js"></script>
  <!-- include spec files here... -->
  <script type="text/javascript" src="spec/awardAgeCalculatorSpec.js"></script>
</head>
<body>
</body>
</html>

Notice that we referenced Underscore as a SUT dependency and we don't need any special test output code to display the results other than ensuring that all required JavaScript files are referenced in the SpecRunner.html file. You can find the example in the underscore.map.reduce-with-jasmine folder from the source code for this chapter.

To run the tests, we just need to open the SpecRunner.html file in a browser and we should see this output:

Adding tests using the default Jasmine infrastructure

Jasmine tests can also be executed automatically through test runners such as Karma (http://karma-runner.github.io/) or Node.js build tools such as Grunt or Gulp. We will discuss these topics in Chapter 5, Using Underscore.js in the Browser, on the Server, and with the Database and Chapter 6, Related Underscore.js Libraries and ECMAScript Standards.

Visually different images
CONTINUE READING
83
Tech Concepts
36
Programming languages
73
Tech Tools
Icon Unlimited access to the largest independent learning library in tech of over 8,000 expert-authored tech books and videos.
Icon Innovative learning tools, including AI book assistants, code context explainers, and text-to-speech.
Icon 50+ new titles added per month and exclusive early access to books as they are being written.
Learning UnderscoreJS
notes
bookmark Notes and Bookmarks search Search in title playlist Add to playlist font-size Font size

Change the font size

margin-width Margin width

Change margin width

day-mode Day/Sepia/Night Modes

Change background colour

Close icon Search
Country selected

Close icon Your notes and bookmarks

Confirmation

Modal Close icon
claim successful

Buy this book with your credits?

Modal Close icon
Are you sure you want to buy this book with one of your credits?
Close
YES, BUY

Submit Your Feedback

Modal Close icon
Modal Close icon
Modal Close icon