© Daniel Curtis 2019
Daniel CurtisPractical Oracle JEThttps://doi.org/10.1007/978-1-4842-4346-6_13

13. Automated Unit Testing

Daniel Curtis1 
(1)
Birmingham, UK
 

Ah, unit testing! It takes a multitude of things coming together to ensure that a team of developers write good automated unit tests for their work. One of these things is deciding on a unit test framework to use (it’s a bit of a maze out there). Another is making it really easy for developers to write their tests.

Unit testing can tend to be an afterthought, and the last thing you want to worry about when you are facing tight deadlines is having to learn a new testing framework on top of the pile of other tasks you likely have on your backlog. This chapter will walk through getting a skeleton unit test framework working in your project—ready for those important automated tests to be written. The examples in the chapter uses the Jasmine testing framework (with Karma as a test runner) within an Oracle JET project. However, you are free to use any unit testing framework you want with Oracle JET, such as QUnit.

Installing Karma and Jasmine

To begin, the necessary libraries must be installed into the project. Run the following command within the UI directory, to install the dependencies for Karma and Jasmine:
npm install karma karma-jasmine karma-chrome-launcher jasmine-core karma-coverage karma-requirejs
Next, install the Karma Command Line Interface (CLI) globally:
npm install -g karma-cli

Karma Setup

Now that the libraries are installed, we can set up the Karma configuration. There is a command (karma init) that can set up this configuration file up with wizard-like questions. However, for this example, we will create and set up the file ourselves, to better understand what it is doing.

Create a new file called karma.conf.js within the UI directory. Inside the file, there are multiple options that we should configure. Let’s walk though these now, and the full code will be included just afterward.

The first option is the basePath, which is the initial starting point for any directory paths within the Karma runner. Leave the basePath as blank.
basePath: ''
Then there are the frameworks that Karma will require. We want to specify these as Jasmine and RequireJS:
frameworks: ['jasmine', 'requirejs']

When Karma runs, it will spin up web browser instances to run the tests, and the files that Karma will be loading into the browser can be set using the files attribute. These patterns are resolved using glob and will have the basePath appended to the beginning. The included: false attribute is required on some of the files, to prevent Karma loading them into a script tag within the browser, and, instead, they will get loaded by RequireJS.

In the following example, we are also excluding the main.js file, as we do not need it to run for our tests. Instead, we will shortly be creating a new main.js file specifically for the test runner.
    files: [
      {pattern: 'web/js/libs/jquery/jquery-3.3.1.js'},
      {pattern: 'web/js/**/*.js', included: false},
      {pattern: 'web/js/jet-composites/**/*', included: false },
      {pattern: 'web/js/viewModels/*.js', included: false},
      {pattern: 'tests/fixtures/**/*.json', included: false},
      {pattern: 'tests/**/*-spec.js', included: false},
      'tests/test-main.js'
    ]
    exclude: [
      'web/js/main.js'
    ],
Next, we want to set up the coverage reporting. We are going to use the karma-coverage library , which uses the Istanbul code coverage tool . Add the following preprocessor block, as well as specifying the reporters and the location to store the reports:
preprocessors: {
      'src/js/viewModels/*.js':['coverage']
},
reporters: ['progress','coverage'],
coverageReporter:{
       type:'html',
       dir:'reports/'
}
Then there are a couple more configurations to set, including port, colors (which enables or disables colors in the reporter and logs), the log level, and whether the files should be auto-watched.
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
Finally, we must specify what browsers the tests should run on. There are a bunch of different browsers and browser launchers that can be used, and it is also possible to run some of the browsers in “headless” mode. Headless mode will open and run the tests in the browser but not obstruct your work by popping open in front of your other windows. It will quietly run in the background. To run in headless mode, you should append Headless to the end of the browser name. For example, Chrome would become ChromeHeadless. I will be using the Chrome browser for my testing, but feel free to change this to your preferred browser.
browsers: ['Chrome'],
That is everything set up for the karma.conf.js file , and the full file should look like Listing 13-1.
// Karma configuration
module.exports = function(config) {
  config.set({
    // base path that will be used to resolve all patterns (eg. files, exclude)
    basePath: '',
    // frameworks to use
    // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
    frameworks: ['jasmine', 'requirejs'],
    // list of files / patterns to load in the browser
    files: [
      {pattern: 'web/js/libs/jquery/jquery-3.3.1.js'},
      {pattern: 'web/js/**/*.js', included: false},
      {pattern: 'web/js/jet-composites/**/*', included: false },
      {pattern: 'web/js/viewModels/*.js', included: false},
      {pattern: 'tests/fixtures/**/*.json', included: false},
      {pattern: 'tests/**/*-spec.js', included: false},
      'tests/test-main.js'
    ],
    // list of files / patterns to exclude
    exclude: [
      'web/js/main.js'
    ],
    // preprocess matching files before serving them to the browser
    // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
    preprocessors: {
      'web/js/viewModels/*.js':['coverage']
    },
    // test results reporter to use
    // possible values: 'dots', 'progress'
    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
    reporters: ['progress','coverage'],
    coverageReporter:{
      type:'html',
      dir:'reports/'
    },
    // web server port
    port: 9876,
    // enable / disable colors in the output (reporters and logs)
    colors: true,
    // level of logging
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: config.LOG_INFO,
    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: true,
    // start these browsers
    // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
    browsers: ['Chrome'],
  })
}
Listing 13-1

The Full karma.conf.js File

test-main Setup

Now, we can move on to the test-main.js file , which is the replacement for the standard main.js file we include in the application. The test-main.js file will be responsible for finding and loading all of the ViewModels and test files within the application. (Test files must be named as the ViewModel name followed by -spec.)

Create a new folder called tests in the UI directory and create test-main.js within that new directory. Listing 13-2 shows the file for our example.
var TEST_REGEXP = /(spec).js$/i;
var VIEWMODEL_REGEXP = /viewModels//
var allTestFiles = [];
var allModules = [];
var normalizedTestModule = function(file) {
    return file.replace(/.js$/, '');
}
// Get a list of all the test files to include
Object.keys(window.__karma__.files).forEach(function(file) {
  if (TEST_REGEXP.test(file)) {
    allTestFiles.push(file);
  } else if(VIEWMODEL_REGEXP.test(file)){
    allModules.push(normalizedTestModule(file))
  }
});
require.config({
  // Karma serves files under /base, which is the basePath from your config file
  baseUrl: '/base/web/js',
  // example of using a couple of path translations (paths), to allow us to refer to different library dependencies, without using relative paths
  paths:
    {
      knockout: 'libs/knockout/knockout-3.4.2.debug',
      jquery: 'libs/jquery/jquery-3.3.1',
      'jqueryui-amd': 'libs/jquery/jqueryui-amd-1.12.1',
      promise: 'libs/es6-promise/es6-promise',
      hammerjs: 'libs/hammer/hammer-2.0.8',
      ojdnd: 'libs/dnd-polyfill/dnd-polyfill-1.0.0',
      ojs: 'libs/oj/v6.0.0/debug',
      ojL10n: 'libs/oj/v6.0.0/ojL10n',
      ojtranslations: 'libs/oj/v6.0.0/resources',
      text: 'libs/require/text',
      signals: 'libs/js-signals/signals',
      customElements: 'libs/webcomponents/custom-elements.min',
      css: 'libs/require-css/css',
      appUtils: 'utils/appUtils',
      Dragibility: 'libs/draggability/draggabilly.pkgd',
      bridget: 'libs/bridget/jquery-bridget',
      'touchr': 'libs/touchr/touchr',
      'trumbowyg': 'libs/trumbowyg/trumbowyg.min',
      'appUtils': 'utils/app-utils',
      'inline-search': 'jet-composites/inline-search/1.0.0'
      },
  // example of using a shim, to load non AMD libraries (such as underscore)
  shim:
  {'jquery':
    {
      exports: ['jQuery', '$']
    }
  },
  // dynamically load all test files
  deps: allTestFiles,
  // we have to kickoff jasmine, as it is asynchronous
  callback: require(allModules, function () {
    window.__karma__.start()
  })
});
Listing 13-2

The test-main.js File

Try running karma start within a new terminal window in the UI directory, to check that everything is running. Chrome should open a new browser window and show results similar to those in Figure 13-1.
../images/468546_1_En_13_Chapter/468546_1_En_13_Fig1_HTML.jpg
Figure 13-1

Karma successfully running

Writing a Test

With the framework in place, it is now possible to start writing tests. To create your first test, add a new file called ticket-desk-spec.js within the UI/tests directory. Inside the new file, add the following code shown in Listing 13-3:
define(['viewModels/ticket-desk'], function (TicketDeskViewModel) {
    describe('Ticket Desk Module - ', function () {
        var viewModel;
        beforeEach(function () {
            viewModel = new TicketDeskViewModel();
        });
        describe('Example Test for onTabRemove Function - ', function () {
            it('Check onTabRemove function runs and passes the tab ID to the delete tab function', function () {
                const deleteTabSpy = spyOn(viewModel, 'deleteTab');
                const event = {
                    detail: {
                        key: 1
                    },
                    preventDefault() { },
                    stopPropagation() { }
                }
                viewModel.onTabRemove(event);
                expect(deleteTabSpy).toHaveBeenCalledWith(1);
            });
        });
    });
});
Listing 13-3

The ticket-desk-spec.js File

This is a really simple test for the onTabRemove function with ticket-desk.js. The code begins by loading the ticket-desk ViewModel within the define block, so that we can run tests against it. The ViewModel instance is initialized before each test from within the beforeEach function . The it block sets up a new test to check whether the deleteTab method is called from within the onTabRemove function, with the correct ID passed into it.

To run the tests, make sure the Karma CLI is still running and that the application is running too. When the files are changed, the Karma CLI should automatically detect changes. All being well, you should receive a success message in the Karma CLI and changing the key value in the event object to another number should fail the test.

When you begin to write more tests for your application, the reports are generated and stored within the UI/reports directory. These reports will give a breakdown of code coverage per ViewModel.

Figure 13-2 shows an overall coverage percentage for all the ViewModels.
../images/468546_1_En_13_Chapter/468546_1_En_13_Fig2_HTML.jpg
Figure 13-2

Overall coverage statistics for all ViewModels

Figure 13-3 shows the functions within a ViewModel that have been covered by the tests. In this figure, you can see that onTabRemove has been covered by our preceding test, but the tabSelectionChanged function has not yet been tested.
../images/468546_1_En_13_Chapter/468546_1_En_13_Fig3_HTML.jpg
Figure 13-3

Code-level coverage report for ticket-desk.js

Summary

In this, the final chapter of Practical Oracle JET, you have explored Jasmine and Karma. You have installed the relevant libraries and set the confirmation of Jasmine and Karma to support a JET application. Finally, you have created your first simple test and should now be in a position to write further tests for the application.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.43.140