Jasmine Cookbook

4 (1 reviews total)
By Munish Sethi
  • Instant online access to over 7,500+ books and videos
  • Constantly updated with 100+ new titles each month
  • Breadth and depth in over 1,000+ technologies
  1. Getting Started with Jasmine Framework

About this book

Jasmine provides a rich set of libraries to design and develop tests for JavaScript applications. Its tests aim to run on any JavaScript-enabled platform and have easy-to-read syntax.

This book is a comprehensive guide to designing and developing Jasmine tests to produce world-class software. The recipes are elaborated using examples from a real-world application, which involves various aspects of designing and developing tests from simple to complex level. You will learn how to create tests for jQuery and Ajax, HTML and JSON fixtures, CoffeeScript, and AngularJS. By learning and applying the best practices throughout this book, you can deliver your applications with zero defects and ensure success for you and your clients.

Publication date:
April 2015
Publisher
Packt
Pages
276
ISBN
9781784397166

 

Chapter 1. Getting Started with Jasmine Framework

In this chapter, we will cover:

  • Writing your first Jasmine test

  • Adding specs to your Jasmine test

  • Adding expectations and matchers to the test

  • Applying different matchers to the Jasmine test

  • Applying setup and teardown functions to the Jasmine test

  • Using the "this" keyword

 

Introduction


Nowadays, JavaScript has become the de facto programming language to build and empower frontend/web applications. We can use JavaScript to develop simple or complex applications. However, applications in production are often vulnerable to bugs caused by design inconsistencies, logical implementation errors, and similar issues. For this reason, it is usually difficult to predict how applications will behave in real-time environments, which leads to unexpected behavior, non-availability of applications, or outages for short or long durations. This generates lack of confidence and dissatisfaction among application users. Also, high cost is often associated with fixing the production bugs. Therefore, there is a need to develop applications that are of a high quality and that offer high availability.

Jasmine plays a vital role to establish effective development process by applying efficient testing processes. Jasmine is an excellent framework for testing JavaScript code both in browser and on the server side. We well use version 2.0.1 of Jasmine in this book. In this chapter, we will cover how tests can be written for JavaScript code using the Jasmine framework. You will also learn how various Jasmine matchers play a pivotal role for writing the tests.

 

Writing your first Jasmine test


To write our first Jasmine test, we will use a program that will calculate the factorial of a given number.

"As a user, I want to validate/evaluate factorial functionality so that I can get the exact factorial value for a given number."

Let's consider some scenarios in the current context:

  • Scenario-1: Factorial value should be evaluated for a given positive number

  • Scenario-2: A 'null' value should be returned for negative numbers

Getting ready

To start writing Jasmine tests, you need to download Jasmine from the official website. Download the Jasmine Standalone release (that is, jasmine-standalone-2.x.x.zip) from the following website:

https://github.com/pivotal/jasmine/releases

How to do it...

To start a Jasmine test, perform the following steps:

  1. First, you need to create a spec file under the /spec folder. Create the Factorial_spec.js file and code the following lines:

    describe("Factorial", function() {
    
    });
  2. Next, add the following code to Factorial_spec.js:

    describe("Factorial", function() {
        it("should get factorial of given number", function() {
    
        });
      });
  3. Further, add another it block to your Jasmine test and use the following code:

    describe("Factorial", function() {
        it("should get factorial of given number", function() {
    
        });
        it("should return null value for passing negative number or less/more than one argument", function() {
    
        });
      });
  4. To implement the factorial functionality, we need JavaScript code. So, create a Factorial.js file and put it under the /src folder:

    function factorial(num)
    {
      if (num==1 && factorial.arguments.length == 1) {
        return 1;
      }
      else if (num >1 && factorial.arguments.length == 1) {
        return num*factorial(num-1);
      }
      else {
        return null;  /* Validate if parameter is passed as negative number or less/more than one parameter */
      }
    }
  5. Now, to test the Factorial functionality, we will implement it in the Jasmine test using the following code:

    describe("Factorial", function() {
        it("should get factorial of given number", function() {
          expect(factorial(3)).toEqual(6);
        });
        it("should return null value for passing negative number or less/more than one arguments", function() {
          expect(factorial(-3)).toEqual(null);
        });
    });

    In the preceding code snapshot, notice that we implemented both scenarios with the help of assertions using expectations and matchers.

    Note

    An expectation in Jasmine is an assertion that is either true or false.

    Jasmine provides the expect function to test the code. It accepts a value called the actual. Furthermore, it is chained with the matcher function, which accepts the expected value. In current context, the matcher function is toEqual.

    To learn more about expectation and matchers, refer to the recipes Adding expectations and matchers to the test and Applying different matchers to the Jasmine test in this chapter.

  6. To run Jasmine's spec for both scenarios, we need to add the reference of the JavaScript file (that is, Factorial.js) and spec file (that is, Factorial_spec.js) to the test runner (that is, SpecRunner.html file). Use the following code:

    <!-- include source files here... -->
      <script type="text/javascript" src="src/Factorial.js"></script>
    
    <!-- include spec files here... -->
      <script type="text/javascript" src="spec/Factorial_spec.js"></script>

    Note

    In your test runner (that is, SpecRunner.html), you can see the reference to different source files (that is, Player.js and Sonj.js) and spec files (that is, SpecHelper.js and PlayerSpec.js). These files are shipped along with the Jasmine standalone release. Here, you need to remove the references to these files and add the reference to your source file (that is, Factorial.js) and spec file (that is, Factorial_spec.js).

  7. Now, execute the test suite by opening the Jasmine runner (that is SpecRunner.html) in a browser, and you will see something similar to the following screenshot, which will let you know that the tests have run successfully:

    The SpecRunner.html file acts as a test runner for Jasmine tests. We can also call it the Jasmine runner. Indeed, from now onwards, throughout this book, we will refer to the SpecRunner.html file as the Jasmine runner.

How it works...

Let's take a look at what we did in this recipe.

In step 1, we defined the test suite using the describe function. It is a global Jasmine function and accepts the following two parameters:

  • String: Usually, we mention this parameter as the name of the test suite corresponding to the functionality that is currently being tested. A test suite represents a specific functionality or component of our application. In our case, it is Factorial.

  • Function: This is a block of code that implements the suite. You can create n suites corresponding to functionality/application components, as needed.

In step 2, we defined the condition of scenario 1 by implementing the it block inside the describe block. It's also a global Jasmine function that accepts two parameters, that is, a string and a function:

  • String: This is the title of the spec. In our case, we created the spec as should get factorial of given number for scenario 1.

  • Function: Here, we write Jasmine code (test code or the actual 'spec'), corresponding to the spec title to test the JavaScript code.

In step 3, we defined another condition for scenario 2 by adding a second spec using an additional it block. You can define as many specs within a test suite as your test requires.

In step 4, to test the JavaScript code, we created the Factorial.js file and wrote code for factorial functionality. We defined the conditions for scenario 1 and scenario 2 in the factorial function.

In step 5, we passed a value to the expect (that is, the expectation) function, which needs to be tested; this value is called the actual value. Furthermore, the expect function is chained to the matcher function, which takes the expected value. On the basis of actual versus expected value, it reports to Jasmine that the spec has either passed or failed.

In step 6, to test and execute the JavaScript code, we included the reference of the JavaScript file (Factorial.js) and the corresponding spec file (Factorial_spec.js) in the SpecRunner.html file.

See also

  • To understand more about specs and how to apply expectations and matchers, refer to the recipes Adding specs to your Jasmine test and Adding expectations and matchers to the test.

    Note

    Jasmine is a Behavior-Driven Development (BDD) framework. However, for now, we are not following the BDD process to write Jasmine tests as it is outside the scope of this recipe. In Chapter 2, Jasmine with TDD and BDD Processes, we will discuss in detail how Jasmine test and application code is developed alongside using Test-Driven Development (TDD) and BDD process.

 

Adding specs to your Jasmine test


To write specs for a given requirement, let's consider the following example of <ABC> company.

<ABC> is a product-based company that develops cutting edge software/products for sales and inventory control systems. Currently, they have one base product that offers all the standard features required of a sales and inventory system (for example, generating sales invoice, sales return/issue, vendor analysis, billing management, budgeting, finance, stock update, and so on). They also customize base products as per customers' specific needs. Recently, the <ABC> company has provided software to a spare parts company and the customer is performing acceptance testing for the inventory system.

"As a Store Administrator, I want to update stock on every new transaction so that I can get the balance/stock in hand for further usage."

Let's consider some scenarios in the current context, that is, updating inventory stock in the event of any new transaction:

  • Scenario-1: Inventory Stock should be updated on account of item(s) sale or issue of item(s)

  • Scenario-2: Inventory stock should be updated on return of any item(s)

  • Scenario-3: Inventory stocks should be updated on receiving/procuring new item(s)

How to do it…

To write specs to a Jasmine test, perform the following steps:

  1. First, you need to create a spec file under the /spec folder. Create the InventoryStock_spec.js file and code the following lines:

    describe("Inventory Stock", function() {
    //Scenario – 1
      
    });
  2. Next, use the following code to define specs:

    describe("Inventory Stock", function() {
    //Scenario – 1
        it("Inventory Stock should be updated on sale/issue of an item", function() {
    
        });
      });
  3. Now, to run the spec defined in the previous step, we need to add the reference of the spec file (that is, InventoryStock_spec.js) to the Jasmine runner (that is, SpecRunner.html file):

    <!-- include spec files here... -->
      <script type="text/javascript" src="spec/InventoryStock_spec.js"></script>
  4. To execute the test suite, open the Jasmine runner in a browser and you will see the spec results, as shown in the following screenshot:

    You can see two things from the execution results:

    • The spec is prefixed with SPEC HAS NO EXPECTATION.

    • The spec passes even if we do not specify any expectation within the it block. In Jasmine, we need to implement an assertion to make the spec pass or fail. An assertion is a comparison between two values/expression that results in a Boolean value. A spec will only be considered passed if the assertion returns the Boolean value as true.

  5. Next, use the following code to optimize step 2:

          describe("Inventory Stock", function() {
            //Scenario - 1
    it("Inventory Stock should be updated on sale of item", function() {
    
              });
    it("Inventory Stock should be updated on issue of an item within organization", function() {
    
              });
          });

    In the preceding code snapshot, you can notice that we further divided the spec into two specs where the first spec represents a sale and the other spec is for the issuing of an item. Now, both the specs represent unique behavior.

    Tip

    It is highly recommended to refactor the requirement up to granular level. This will help you to analyze test execution results. Moreover, you (and other stakeholders) can easily identify root causes and map precisely the failed specs with application code.

  6. Next, let's use the following test code to implement the specs functionality:

          describe("Inventory Stock", function() {
          //Scenario - 1
    it("Inventory Stock should be updated on sale of item", function() {
                var stockinhand_item1=11;
                var item1 = 1;
                var transaction = 'SALE';
                expect(stockinhand_item1-item1).toEqual(10);
              });
    it("Inventory Stock should be updated on issue of an item within organization", function() {
                var stockinhand_item1=11;
                var item1 = 1;
                var transaction = 'ISSUE';
                expect(stockinhand_item1-item1).toEqual(10);
              });
          });
  7. Now when you run the spec file, you will see that both the specs pass for scenario 1, as shown in the following screenshot:

  8. Now, use the following code to define and implement the specs for scenario 2 and scenario 3:

    describe("Inventory Stock", function() {
      //Scenario - 1
      it("Inventory Stock should be updated on sale of item", function() {
          var stockinhand_item1=11;
          var item1 = 1;
          var transaction = 'SALE';
          expect(stockinhand_item1-item1).toEqual(10);
        });
        it("Inventory Stock should be updated on issue of an item within organization", function() {
          var stockinhand_item1=11;
          var item1 = 1;
          var transaction = 'ISSUE';
          expect(stockinhand_item1-item1).toEqual(10);
        });
        //Scenario - 2
        it("Inventory Stock should be updated on return of any item", function() {
          var stockinhand_item1=11;
          var item1 = 1;
          var transaction = 'SALE RETURN';
          expect(stockinhand_item1+item1).toEqual(12);
        });
        //Scenario - 3
        it("Inventory Stock should be updated on receiving or procuring new item", function() {
          var stockinhand_item1=11;
          var item1 = 1;
          var transaction = 'PROCUREMENT';
          expect(stockinhand_item1+item1).toEqual(12);
        });
    });
  9. Finally, run the spec file (InventoryStock_spec.js) using the Jasmine runner. You will see the test execution results, as shown in the following screenshot indicating the success of all four specs:

How it works...

Let's understand what we did throughout this recipe.

In step 1, we created a spec file and defined the name of the test suite corresponding to the functionality, which is currently being tested. In the present scenario, we named it as Inventory Stock.

In steps 2 to 4, we created the spec to define scenario 1 and executed the scenario using the Jasmine runner. In this scenario, we defined specs to validate whether the stock is being updated (or not) on account of the sale of an item or the issue of an item to a person/department within the organization.

In step 5, we further refactored the specs of scenario 1 to make them more understandable and granular.

In steps 6 and 7, we implemented the test code for scenario 1 corresponding to specs.

In steps 8 and 9, following the same pattern, we implemented the test code for scenarios 2 and 3.

See also

  • To gain a deeper understanding about how to design and write specs, refer to the recipe Defining nested suites to write more meaningful specs in Chapter 2, Jasmine with TDD and BDD Processes.

 

Adding expectations and matchers to the test


Inside the it block, you can write all the test code that is required to test the application/JavaScript code by applying assertions using expectations and matchers. Expectations are built with an expect function, which takes a value called actual. It is further chained with the matcher function(s), which takes the expected value. Each matcher implements a Boolean expression depending on the actual and expected value. It is responsible for reporting to Jasmine whether the expectation is true or false. Jasmine passes or fails the spec on the basis of the Boolean value returned by the matcher. In this recipe, you will learn how assertions are applied using the toBe matcher. You will also learn how negative assertions are applied.

To understand this recipe, assume that you are developing a bank application to track details of fixed deposit, recurring deposit, and all other financial transactions.

"As a finance administrator, I want to track all financial transactions so that I can categorize them for further assessment/processing."

Let's consider the following scenarios in the current context, that is, all financial transactions should be tracked and categorized:

  • Scenario-1: Deposit should be of the fixed Deposit (FD) type on locking amount for a fix period

  • Scenario-2: Deposit should be of the Recurring Deposit (RD) type for an amount deposited with regular frequency (that is, monthly, quarterly, half-yearly, yearly, and so on)

How to do it…

You need to perform the following steps to apply the toBe matcher on these scenarios:

  1. Create the Deposit_spec.js file under the /spec folder and code the following lines:

    describe("Bank Deposit ", function() {
    //Scenario 1
      
    });
  2. Next, use the following code to define specs for scenario 1 and scenario 2:

    describe("Bank Deposit",function(){
      //Scenario 1
      it("should be considered as FD on locking amount for a fixed period", function(){
    
      });
      //Scenario 2
      it("should be considered as RD on depositing amount on regular frequency", function(){
    
      });  
    });
  3. To implement scenario 1 and scenario 2, we need JavaScript code. So, create the Deposit.js file, put it under the /src folder, and use the following code:

    function Deposit(Frequency) {
      this.Type= Frequency;
    };
    
    Deposit.prototype.BankDeposit = function(){
      switch (this.Type) {
      case "FIX" :  
            return "FD";
            break;
      case "RECURRING" :  
            return "RD";
            break;
      };
    };
  4. Next, use the following code to implement specs for scenario 1 and scenario 2:

    describe("Bank Deposit",function(){
      //Scenario -1
      it("should be considered as FD on locking amount for a fix period", function(){
        var MyDeposit = new Deposit("FIX");
        DepositType = MyDeposit.BankDeposit();
        expect(DepositType).toBe("FD");
      });
      //Scenario -2
      it("should be considered as RD on depositing amount on regular frequency", function(){
        var MyDeposit = new Deposit("RECURRING");
        DepositType = MyDeposit.BankDeposit();
        expect(DepositType).toBe("RD");
      });  
    });
  5. Now, run the spec file (Deposit_spec.js) using the Jasmine runner and you will see that tests pass for both the scenarios, as shown in the following screenshot:

  6. Now, to apply negative assertions on both of the scenarios, consider the following code:

    describe("Bank Deposit",function(){
      //Scenario -1
      it("should be considered as FD on locking amount for a fix period", function(){
        var MyDeposit = new Deposit("FIX");
        DepositType = MyDeposit.BankDeposit();
        expect(DepositType).toBe("FD");
        expect(DepositType).not.toBe("FD");
      });
      //Scenario -2
      it("should be considered as RD on depositing amount on regular frequency", function(){
        var MyDeposit = new Deposit("RECURRING");
        DepositType = MyDeposit.BankDeposit();
        expect(DepositType).toBe("RD");
        expect(DepositType).not.toBe("RD");
      });  
    });

    In the preceding code snapshot, notice that we implemented the negative assertion by chaining the call to expect with a not before calling the matcher.

  7. Now, run the spec file (Deposit_spec.js) using the Jasmine runner and you will see that it indicates that both the tests fail, as shown in the following screenshot:

    In the preceding screenshot, notice that we have provided wrong values corresponding to negative assertions.

  8. Now, use the following code to pass both the tests:

    describe("Bank Deposit",function(){
      //Scenario -1
      it("should be considered as FD on locking amount for a fix period", function(){
        var MyDeposit = new Deposit("FIX");
        DepositType = MyDeposit.BankDeposit();
        expect(DepositType).toBe("FD");
        expect(DepositType).not.toBe("RD");
        expect(DepositType).not.toBe("Any value Other than 'FD' ");
      });
      //Scenario -2
      it("should be considered as RD on depositing amount on regular frequency", function(){
        var MyDeposit = new Deposit("RECURRING");
        DepositType = MyDeposit.BankDeposit();
        expect(DepositType).toBe("RD");
        expect(DepositType).not.toBe("FD");
        expect(DepositType).not.toBe("Any value Other than 'RD' ");
      });  
    });
  9. Finally, run the spec file (Deposit_spec.js) using the Jasmine runner and you will see that it indicates that both the tests pass, as shown in the following screenshot:

How it works...

In step 1 and step 2, we defined the name of the suite and specs for scenario 1 and scenario 2.

In step 3, JavaScript code is provided to implement Jasmine tests for both the scenarios. Here, we defined the object construction function with one parameter (that is, frequency) to identify the type of deposit. Also, we created a BankDeposit() function for the deposit object using JavaScript prototype property.

In step 4, we implemented the test code corresponding to specs within the it block to test the code. First, we created the object of deposit and then invoked the BankDeposit() function of the deposit object to get the deposit type. Finally, we implemented the assertion to compare the actual and expected value using the toBe matcher.

In steps 6 through 9, we implemented a negative assertion by chaining the call to expect with a not before calling the matcher. We also looked at how Jasmine tests pass/fail using the different values with negative assertion.

 

Applying different matchers to the Jasmine test


Jasmine provides a rich set of matchers to test JavaScript code. In this recipe, you will learn to apply various matchers in different situations.

To understand this recipe, let's assume that you are developing an application and you have to implement test code for various scenarios by applying different Jasmine matchers.

"As a developer, I want to apply different Jasmine matchers so that I can implement a test condition successfully."

Let's consider some scenarios in the preceding context, that is, where Jasmine matchers should be applied for different test conditions:

  • Scenario-1: The 'toMatch' matcher should be applied successfully for regular expressions

  • Scenario-2: The 'toEqual' matcher should be applied successfully for literals, variables, and objects

  • Scenario-3: The 'toBe' matcher should be applied successfully for literals, variables, and objects

  • Scenario-4: The 'toBeDefined' matcher should be applied successfully to compares against defined

  • Scenario-5: The 'toBeUndefined' matcher should be applied successfully to compares against undefined

  • Scenario-6: The 'toBeNull' matcher should be applied successfully to compare against null

  • Scenario-7: The 'toBeTruthy' matcher should be applied successfully for Boolean casting testing

  • Scenario-8: The 'toBeFalsy' matcher should be applied successfully for Boolean casting testing

  • Scenario-9: The 'toContain' matcher should be applied successfully for finding an item in an array

  • Scenario-10: The 'toBeLessThan' matcher should be applied successfully for mathematical comparisons

  • Scenario-11: The 'toBeGreaterThan' matcher should be applied successfully for mathematical comparisons

  • Scenario-12: The 'toBeCloseTo' matcher should be applied for precision math comparison

How to do it…

To apply different matchers to your Jasmine tests, you need to perform the following steps in the preceding scenarios:

  1. Create the JasmineMatchers_spec.js file under the /spec folder and code the following lines:

    describe("Jasmine Matchers", function() {
    //Scenario – 1
      
    });
  2. Now, use the following code to define and implement the spec for scenario 1 using the toMatch matcher:

    describe("Jasmine Matchers",function(){
      //Scenario -1
      it("'toMatch' matcher should be applied successfully for regular expressions", function() {
          var strString1 = "Packt Cookbooks are an excellent source of learning";
          var strPhone = "001-789-56-67";
          expect(strString1).toMatch(/Cookbooks/);
          expect(strString1).toMatch(/cookbooks/i);
          expect(strString1).not.toMatch(/Java/);
          expect(strPhone).toMatch(/\d{3}-\d{3}-\d{2}-\d{2}/);
        });
    });

    Note

    A regular expression is a sequence of characters that forms a search pattern. Search patterns can be defined based on a single character, combination of characters/strings, or more complicated patterns. To explore more about regular expressions in greater depth, visit the following website:

    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions

  3. The next step is to run the spec file (JasmineMatchers_spec.js) using the Jasmine runner, and you will see that the test passes, as shown in the following screenshot:

  4. Now, use following code to implement scenario 2 and scenario 3:

    describe("Jasmine Matchers",function(){
    
      //Scenario - 2
      describe("toEqual matcher should be applied successfully", function(){
        it("if numbers are equal", function() {
            var intVar = 15;
            expect(intVar).toEqual(15);
          });
        it("if strings are equal", function() {
            var strVar = "Jasmine Cookbook";    
            expect(strVar).toEqual("Jasmine Cookbook");
          });
    
        it("if objects are equal", function() {
            var MyObectj1 = {a: 12, b: 13};
            var MyObectj2 = {a: 12, b: 13};    
            expect(MyObectj1).toEqual(MyObectj2);
            expect(MyObectj1.a).toEqual(MyObectj2.a);
            expect(MyObectj1.a).not.toEqual(MyObectj2.b);
          });
        it("if arrays are equal", function() {
            expect([8, 9, 10]).toEqual([8, 9, 10]);
            expect([8, 9, 10, 11]).not.toEqual([8, 9, 10]);
          });
      });
    
      //Scenario - 3
      it("toBe matcher should be applied successfully for literals, variables and objects", function() {
          var MyObj = {foo: "foo"};
          var MySameObj = {foo: "foo"};
          var strVar = "Jasmine Cookbook";
          var myArr = [8, 9, 10];
          expect(MyObj).toBe(MyObj);
          expect(MySameObj).not.toBe(MyObj);
    
      expect(MySameObj).toEqual(MyObj);
          expect(strVar).toBe("Jasmine Cookbook");
          expect(myArr).toEqual([8, 9, 10]);
          expect(myArr).not.toBe([8, 9, 10]);
      });
    });

    In the preceding code snapshot, notice that we created two objects (that is, MyObj and MySameObj). Both look similar and equal, but they are two different objects with exactly the same attributes. Furthermore, you can observe the behavior of the toBe and toEqual matchers. Here, while comparing both the objects, the assertion value will return true with the toEqual matcher and false with the toBe matcher. Also, this is true for an array object (that is, myArr).

    Note

    The toEqual() matcher checks equivalence. On the other hand, the toBe() matcher ensures that they are the exact same objects.

  5. Next, run the spec file (JasmineMatchers_spec.js) for scenario 2 and scenario 3 using the Jasmine runner. The tests should run successfully, as shown in the following screenshot:

  6. Use the following code to implement scenario 4:

    describe("Jasmine Matchers",function(){
      //Scenario - 4
      it("toBeDefined should be applied successfully to compares against defined.", function() {
            var MyObj = {
              foo: "foo"
            };
            var Myfunction = (function() {})();
            var strUndefined;
            expect("Jasmine Cookbooks").toBeDefined();
            expect(MyObj).toBeDefined();
            expect(MyObj.foo).toBeDefined();
            expect(Myfunction).not.toBeDefined();
            expect(strUndefined).not.toBeDefined();
          
        });
    });

    Note

    Undefined is a built-in JavaScript type. In JavaScript, if we declare a variable without assigning a value, its type is undefined. Also, JavaScript functions without a return statement or with empty return statements return undefined. To learn more about undefined and how it works, visit the following website:

    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/undefined

  7. Use the following code to implement scenario 5:

    describe("Jasmine Matchers",function(){
      //Scenario - 5
      it("toBeUndefined should be applied successfully to compares against undefined.", function() {
          var MyObj = {
                  foo: "foo"
                };
          var Myfunction = (function() {})();
          var strUndefined;
            expect(MyObj).not.toBeUndefined();
            expect(MyObj.foo).not.toBeUndefined();      
            expect(Myfunction).toBeUndefined();
            expect(strUndefined).toBeUndefined();
      });
    });
  8. Now, run the spec file (for scenario 4 and 5) with the Jasmine runner. You will see that all Jasmine tests pass for both the scenarios, as shown in the following screenshot:

  9. To implement scenario 6, use the following code:

    describe("Jasmine Matchers",function(){
       //Scenario - 6
        it("toBeNull matcher should be applied successfully to compare against null", function() {
            var nullValue = null;
            var valueUndefined;
            var notNull = "notNull";
            expect(null).toBeNull();
            expect(nullValue).toBeNull();
            expect(valueUndefined).not.toBeNull();
            expect(notNull).not.toBeNull();
          });   
    });
  10. To see how Jasmine handles null values using the toBeNull matcher, run the spec file (only for scenario 6) with the Jasmine runner. You will see that the test passes, as shown in the following screenshot:

  11. Use the following code to implement scenarios 7 and 8:

    describe("Jasmine Matchers",function(){
        //Scenario - 7
           it("toBeTruthy matcher should be applied successfully for Boolean casting testing", function() {
            var MyVar1=12, MyVar2 = "True for Non Empty Strings";
            expect(true).toBeTruthy();
            expect("Jasmine Cookbook").toBeTruthy();
            expect(MyVar1).toBeTruthy();
            expect(MyVar2).toBeTruthy();
        });
        //Scenario - 8
        it("toBeFalsy matcher should be applied successfully for Boolean casting testing", function() {
            var MyVar1=12, MyVar2 = "True for Non Empty Strings";
            expect(false).toBeFalsy();
            expect(null).toBeFalsy();
            expect(true).not.toBeFalsy();
            expect("Jasmine Cookbook").not.toBeFalsy();
            expect(MyVar1).not.toBeFalsy();
            expect(MyVar2).not.toBeFalsy();
        });
    });
  12. Next, run the spec file (for scenarios 7 and 8) with the Jasmine runner and you will see that both the Jasmine tests pass, as shown in the following screenshot:

  13. Use the following code to implement scenario 9:

    describe("Jasmine Matchers",function(){
        it("toContain matcher should be applied successfully for finding an item in an Array", function() {
          var MyArray = ["Jasmine", "Cookbook", "JavaScript"];
            expect([1, 2, 3]).toContain(2);
            expect([1, 2, 3]).toContain(2,3);
            expect(MyArray).toContain("Cookbook");
            expect([1, 2, 3]).not.toContain(4);
            expect(MyArray).not.toContain("Java");
        });
    });
  14. Now, run the spec file (only for scenario 9) with the Jasmine runner and you will see that all the test conditions pass for scenario 9, as shown in the following screenshot:

  15. Use the following code to implement scenarios 10 and 11:

    describe("Jasmine Matchers",function(){
        //Scenario - 10
        it("toBeLessThan matcher should be applied successfully for mathematical comparisons", function() {
            var pi = 3.1415926, g = 9.71; num1=5, num2=9;
            expect(pi).toBeLessThan(g);
            expect(num1).toBeLessThan(num2);
            expect(g).not.toBeLessThan(pi);
            expect(num2).not.toBeLessThan(num1);
        });
    
        //Scenario - 11
        it("toBeGreaterThan matcher should be applied successfully for mathematical comparisons", function() {
            var pi = 3.1415926, g = 9.71; num1=5, num2=6;
            expect(g).toBeGreaterThan(pi);
            expect(num2).toBeGreaterThan(num1);
            expect(pi).not.toBeGreaterThan(g);
            expect(num1).not.toBeGreaterThan(num2);
        });
    });
  16. Run the spec file (for scenarios 10 and 11) with the Jasmine runner and you will see that both the tests pass, as shown in the following screenshot:

  17. To implement scenario 12, use the following code:

    describe("Jasmine Matchers",function(){
        it("toBeCloseTo matcher should be applied for precision math comparison", function() {
            var pi = 3.1415926, e = 2.78;
            expect(pi).not.toBeCloseTo(e);
            expect(pi).toBeCloseTo(e,0);
            expect(4.334).toBeCloseTo(4.334);
            expect(4.334).toBeCloseTo(4.3345,1);
            expect(4.334).toBeCloseTo(4.3345,2);
            expect(4.334).not.toBeCloseTo(4.3,2);
            expect(4.223).not.toBeCloseTo(4.22,3);
            expect(4.223).not.toBeCloseTo(4.22,4);
        });
    });
  18. Next, run the spec file (for scenario 12) with the Jasmine runner and you will see that all the test conditions pass:

  19. Finally, to run all the 12 scenarios in one go, make a single spec file with the entire test code and run it (JasmineMatchers_spec.js) with the Jasmine runner. You will see that all the tests pass:

How it works...

Let's take a look at the steps of this recipe.

In steps 1 to 3, we implemented scenario 1 with the toMatch matcher. It checks whether something is matched for a regular expression. You can use the toMatch matcher to test search patterns. In our case, we implemented the test code to find out search patterns with different regular expressions.

In steps 4 and 5, we defined the specs for scenario 2 and 3, and we implemented the test code corresponding to specs using the toEqual and toBe matchers. Here, notice that toBe looks similar to toEqual, but that is not the case. The toBe matcher returns the value true if the objects are equal. For example, in our case, MyObj and MySameObj look like the same objects, but in fact both are different objects with exactly the same attribute/behavior. Therefore, the assertion will return a true value with the toEqual matcher, but a false value with the toBe matcher.

In steps 6 to 8, we implemented scenarios 4 and 5 and saw how the toBeDefine and toBeUndefine matchers are applied to test JavaScript's Undefined type. In step 7, we first implemented a test condition for a non-empty string and two test conditions with object variable MyObj. We also implemented test conditions for the strUndefined variable and the MyFunction()function by applying negative assertions. Conversely, in step 8, we implemented test conditions with the object variable MyObj by applying negative assertions.

In step 9, we implemented test code for scenario 6 using the toBeNull matcher. In step 10, we saw test conditions pass for null values. However, we applied negative assertions to pass test conditions for not Null and Undefined values.

In step 11, we implemented scenario 7 and scenario 8 using the toBeTruthy and toBeFalsy matchers. We use the toBeTruthy matcher to check whether something returns/evaluates to true. Similarly, we use the toBeFalsy matcher to check whether something returns/evaluates to false. In our case, we applied the toBeTruthy matcher for true value, non-empty strings and numbers other than zero. Similarly, we applied toBeFalsy matcher to validate false, null, and empty strings.

In step 13, we implemented the test code for scenario 9 using the toContain matcher. Here, we implemented test conditions to find out an element(s)/item(s) of an array using the toContain matcher. Similarly, we implemented test conditions to check if an element/an item does did not exist in an array by applying negative assertions.

In step 15, we implemented scenario 10 and scenario 11 to compare mathematical values using the toBeLessThan and toBeGreaterThan matchers.

In step 17, we implemented scenario 12 using the toBeCloseTo matcher. This matcher is used to check whether a number is close to another number, up to a given level of decimal precision. In our case, we checked whether the expected number was equal to the actual number with a given level of decimal precision.

 

Applying setup and teardown functions to the Jasmine test


Very often, we reuse pieces of code across different scenarios. This is due to functionality dependencies among scenarios, preconditions, or some other requirements such as initialization/declaration of application/system variables or objects. This improves code redundancy and maintainability.

Generally, to avoid code duplication (across scenarios/Jasmine specs) and increase code reusability and readability, we use setup and teardown functions. Jasmine provides two global functions (that is, beforeEach and afterEach) corresponding to setup and teardown functions. We can initialize variables and write common code and preconditions under the beforeEach function. Similarly, the afterEach function can be used to reinitialize variables or reset preconditions. The beforeEach function is called once before each spec is run in the describe block, and the afterEach function is called once after each spec is run. Both the functions are very useful for refactoring and optimizing the common code.

Getting ready

You will learn this recipe with the help of the second recipe in this chapter. For more information, refer to the Adding specs to your Jasmine test recipe. In this recipe, we implemented three scenarios for a sales and inventory control system and created a spec file (InventoryStock_spec.js) with the test code.

How to do it…

To apply Setup and Teardown to the Jasmine test, you need to perform the following steps:

  1. First, you need to create a spec file (InventoryStockOptimizeCode_spec.js) under the /spec folder and get the following code from the spec file (InventoryStock_spec.js) created in the second recipe of this chapter, Adding specs to your Jasmine test:

    describe("Inventory Stock", function() {
      //Scenario - 1
      it("Inventory Stock should be updated on sale of item", function() {
            var stockinhand_item1=11;
            var item1 = 1;
          var transaction = "SALE";
          expect(stockinhand_item1-item1).toEqual(10);
        });
        it("Inventory Stock should be updated on issue of an item within organization", function() {
            var stockinhand_item1=11;
            var item1 = 1;
          var transaction = "ISSUE";
          expect(stockinhand_item1-item1).toEqual(10);
        });
    
        //Scenario - 2
        it("Inventory Stock should be updated on return of any item", function() {
            var stockinhand_item1=11;
            var item1 = 1;
          var transaction = "SALE RETURN";
          expect(stockinhand_item1+item1).toEqual(12);
        });
    
        //Scenario - 3
        it("Inventory Stock should be updated on receiving or procuring new item", function() {
            var stockinhand_item1=11;
            var item1 = 1;
          var transaction = "PROCUREMENT";
          expect(stockinhand_item1+item1).toEqual(12);
        });
    });

    In the preceding code snapshot, notice the code redundancy across the specs. Here, we declared and assigned value to variables in each spec separately.

  2. Next, refactor the code by applying the beforeEach and afterEach function by using the following code:

    describe("Inventory Stock", function() {
      var stockinhand_item1, item1;
      beforeEach(function() {
          stockinhand_item1=11, item1 = 1;
        console.log("beforeEach: Stock in hand for item1 before spec execution = " + stockinhand_item1);
        });
      afterEach(function() {
          stockinhand_item1=0, item1 = 0;
        console.log("afterEach: Stock in hand for item1 once spec executed = " + stockinhand_item1);
        });
    
      //Scenario - 1
      it("Inventory Stock should be updated on sale of an item", function() {    
          expect(stockinhand_item1-item1).toEqual(10);
        });
        it("Inventory Stock should be updated on issue of an item within organization", function() {
            expect(stockinhand_item1-item1).toEqual(10);
        });
    
        //Scenario - 2
        it("Inventory Stock should be updated on return of an item", function() {
            expect(stockinhand_item1+item1).toEqual(12);
        });
    
        //Scenario - 3
        it("Inventory Stock should be updated on receiving or procuring new item", function() {
            expect(stockinhand_item1+item1).toEqual(12);
        });
    });

    In the preceding code snapshot, notice that we declared the common variables and assigned the corresponding values in the beforeEach function. Here, we have written the code just for illustrative purpose and to understand the working of beforeEach and afterEach function.

  3. Finally, run the spec file (InventoryStockOptimizeCode_spec.js) using the Jasmine runner (that is, SpecRunner.html). You will see test execution results, as shown in the following screenshot, which indicates that all four specs are passing:

    In your browser, if you go to the console window, you will see that the message defined with the console.log() method is printed four times, corresponding to each spec.

How it works...

In steps 1 to 3, we looked at how setup/teardown functions are applied to Jasmine tests using the beforeEach and afterEach functions. In step 2, we declared both the variables (stockinhand_item1 and item1) at the top-level scope, that is, the describe block. Here, we refactored the test code by moving the initialization code into a beforeEach function. Also, we reinitialized the value of variables using the afterEach function.

 

Using the "this" keyword


In Jasmine, we can also initialize and share the variables between beforeEach, it, and afterEach using the this keyword. In this recipe, we will see how the this keyword can be used within the beforeEach or afterEach functions.

Getting ready

You will learn this recipe with the help of the previous recipe. For more information, refer to the previous recipe, Applying setup and teardown functions to the Jasmine test. In this recipe, we refactor three scenarios for a sales and inventory control system by initializing/putting together common code in the beforeEach and afterEach functions.

How to do it…

To apply the this keyword to your Jasmine test, you need to perform the following steps:

  1. First, you need to create a spec file (InventoryStockOptimizeCode_With_this_spec.js) under the /spec folder and get the following code from the spec file (InventoryStockOptimizeCode_spec.js) created in the previous recipe, Adding specs to your Jasmine test:

    describe("Inventory Stock", function() {
      var stockinhand_item1, item1;
      beforeEach(function() {
          stockinhand_item1=11, item1 = 1;
        });
      afterEach(function() {
          stockinhand_item1=0, item1 = 0;
        });
      //Scenario - 1
      it("Inventory Stock should be updated on sale of an item", function() {    
          expect(stockinhand_item1-item1).toEqual(10);
        });
        it("Inventory Stock should be updated on issue of an item within organization", function() {
            expect(stockinhand_item1-item1).toEqual(10);
        });
        //Scenario - 2
        it("Inventory Stock should be updated on return of an item", function() {
            expect(stockinhand_item1+item1).toEqual(12);
        });
        //Scenario - 3
        it("Inventory Stock should be updated on receiving or procuring new item", function() {
            expect(stockinhand_item1+item1).toEqual(12);
        });
    });
  2. Now, apply the this keyword by using the following code:

    describe("Inventory Stock", function() {
      beforeEach(function() {
          this.stockinhand_item1=11, this.item1 = 1;
          console.log("beforeEach: Stock in hand for item1 before spec execution = " + this.stockinhand_item1);      
        });
      afterEach(function() {
          this.stockinhand_item1=0, this.item1 = 0;
          console.log("afterEach: Stock in hand for item1 once spec executed = " + this.stockinhand_item1);
        });
    
      //Scenario - 1
      it("Inventory Stock should be updated on sale of an item", function() {    
          this.transactionType = "SALE";
          expect(this.stockinhand_item1-this.item1).toEqual(10);
          expect(this.transactionType).toBeDefined();
        });
        it("Inventory Stock should be updated on issue of an item within organization", function() {
            expect(this.stockinhand_item1-this.item1).toEqual(10);
          expect(this.transactionType).toBeUndefined();
        });
    
        //Scenario - 2
        it("Inventory Stock should be updated on return of an item", function() {                expect(this.stockinhand_item1+this.item1).toEqual(12);
          expect(this.transactionType).toBeUndefined();
        });
    
        //Scenario - 3
        it("Inventory Stock should be updated on receiving or procuring new item", function() {        
      expect(this.stockinhand_item1+this.item1).toEqual(12);
      expect(this.transactionType).toBeUndefined();
        });
    });
  3. Finally, run the spec file (InventoryStockOptimizeCode_With_this_spec.js) using the Jasmine runner (that is, SpecRunner.html). You should see test execution results, as shown in the following screenshot, indicating that all four tests are passing:

How it works...

In steps 1 to 3, we looked at how the this keyword is applied to a Jasmine test. In step 2, we removed the variable declaration from the top-level scope (that is, the describe block) and initialized/reinitialized the variables into the beforeEach/afterEach functions using the this keyword. Also, notice that this.transactionType is true only for the spec in which it was defined. For the other specs, the this.transactionType variable is considered as undefined. In other words, the scope of the this.transactionType variable is limited to the first spec only (that is, Inventory Stock should be updated on sale of an item). Conversely, the this.item1 variable is considered defined for all the specs because it is assigned in the beforeEach function that runs each time.

About the Author

  • Munish Sethi

    Munish Sethi is a postgraduate in computer science and MBA in IT from SCDL. He has 18 years of industry experience and has a sound knowledge of test automation, test- and behavior-driven development, white box testing, big data testing, review techniques, test estimation, and requirement analysis. Currently, he is working with Impetus Infotech Pvt. Ltd. where he plays the role of test craftsman and automation architect. With his diverse experience, he helps engineering teams with deliveries and also with designing test solutions and formulating test strategies for his clients. He has also designed and implemented many automation frameworks (such as modular, hybrid, data-driven, keyword-driven, and page-object models) using Jasmine/Protractor, QTP, Selenium Web driver, Junit, TestNG, SOAPUI, Sikuli, Test Complete, and so on. He's very keen to explore new and upcoming tools and technologies.

    He has worked with both product- and services-based organizations. Munish's Twitter handle is @munishksethi.

    Browse publications by this author

Latest Reviews

(1 reviews total)
Ausführlich und umfassendes Standardwerk

Recommended For You

Book Title
Access this book, plus 7,500 other titles for FREE
Access now