Книга: Angular 2 Cookbook
Назад: 9. Angular 2 Testing
Дальше: Writing a minimum viable unit test suite for a simple component

Creating a minimum viable unit test suite with Karma, Jasmine, and TypeScript

Before you jump into the intricacies of testing an Angular 2 application, it's important to first examine the supporting infrastructure that will make running these tests possible. The bulk of official Angular resources offer tests on top of Karma and Jasmine, and there's no reason to rock the boat on this one, as these are both fine testing tools. That said, it's a whole new world with TypeScript involved, and using them in tests will require some considerations.

This recipe will demonstrate how to put together a very simple unit test suite. It will use Karma and Jasmine as the test infrastructure, TypeScript and Webpack for compilation and module support, and PhantomJS as the test browser. For those unfamiliar with these tools, here's a bit about them:

  • Karma is a unit test runner. You run tests through Karma on the command line. It has the ability to start up a test server that understands how to find test files and serve them to the test browser.
  • Jasmine is a test framework. When you use keywords such as "it" and "describe," remember that they are part of Jasmine unit tests. It integrates with Karma and understands how to expose and run the tests you've written.
  • PhantomJS is a headless webkit browser. (Headless means it runs as a process that does not have a visible user interface but still constructs a DOM and has a JS runtime.) Unit tests require a browser to run, as the JavaScript unit tests are designed to execute inside a browser runtime. Karma supports a large number of browser plugins to run the tests on, including standard browsers such as Chrome and Firefox. If you were to incorporate these browser plugins, Karma would start up an instance of the browser and run the tests inside it. For the purpose of creating a minimum viable unit test suite, you are fine doing the testing inside a headless browser, which will cleanly report its results to the command line. If you want to run your tests inside an actual browser, Karma will expose the server at a specified port, which you can access directly, for example, visiting http://localhost:9876 in the desired test browser.

Note

The code, links, and a live example related to this recipe are available at .

Getting ready

Start out with a package.json file:

[package.json]      {}   

Note

This still needs to be a valid JSON file, as npm needs to be able to parse it and add to it.

How to do it...

Start off by creating the file that will be tested. You intend to use TypeScript, so go ahead and use its syntax here:

[src/article.ts]      export class Article {     title:string =        "Lab Mice Strike for Improved Working Conditions, Benefits"   }   

Writing a unit test

With the Article class defined, you can now import it into a new test file, article.spec.ts, and use it.

Note

Jasmine test files, by convention, are suffixed with .spec.ts. Test files generated by the Angular CLI will exist alongside the file they test, but by no means is this mandatory. You can define your convention inside your Karma configuration later on.

Start off by importing the Article class and create an empty Jasmine test suite using describe:

[src/article.spec.ts]      import {Article} from './article';      describe('Article unit tests', () => {   });   

Note

describe defines a spec suite, which includes a string title called Article unit tests, and an anonymous function, which contains the suite. A spec suite can be nested inside another spec suite.

Inside a describe suite function, you can define beforeEach and afterEach, which are functions that execute before and after each unit test is defined inside the suite. Therefore, it is possible to define nested setup logic for unit tests using nested describe blocks.

Inside the spec suite function, write the unit test that is using it:

[src/article.spec.ts]      import {Article} from './article';      describe('Article unit tests', () => {     it('Has correct title', () => {       let a = new Article();       expect(a.title)         .toBe("Lab Mice Strike for Improved Working Conditions,        Benefits");     });   });   

Note that both the code and the test are written in TypeScript.

Configuring Karma and Jasmine

First, install Karma, the Karma CLI, Jasmine, and the Karma Jasmine plugin:

 npm install karma jasmine-core karma-jasmine --save-dev npm install karma-cli -g 

Alternately, if you want to save a few keystrokes, the following is equivalent:

 npm i -D karma jasmine-core karma-jasmine npm i karma-cli -g 

Karma reads its configuration out of a karma.conf.js file, so create that now:

[karma.conf.js]      module.exports = function(config) {     config.set({     })   }   

Karma needs to know how to find the test files and also how to use Jasmine:

[karma.conf.js]      module.exports = function(config) {     config.set({       frameworks: [         'jasmine'       ],       files: [         'src/*.spec.js'       ],       plugins : [         'karma-jasmine',        ]     })   }   

Configuring PhantomJS

PhantomJS allows you to direct tests entirely from the command line, but Karma needs to understand how to use PhantomJS. Install the PhantomJS plugin:

 npm install karma-phantomjs-launcher --save-dev 

Next, incorporate this plugin into the Karma config:

[karma.conf.js]      module.exports = function(config) {     config.set({       browsers: [         'PhantomJS'       ],       frameworks: [         'jasmine'       ],       files: [         'src/*.spec.js'       ],       plugins : [         'karma-jasmine',          'karma-phantomjs-launcher'       ]     })   }   

Karma now knows it has to run the tests in PhantomJS.

Compiling files and tests with TypeScript

If you're paying attention closely, you'll note that the Karma config is referencing test files that do not exist. Since you're using TypeScript, you must create these files. Install TypeScript and the Jasmine type definitions:

 npm install typescript @types/jasmine --save-dev 

Add script definitions to your package.json:

[package.json]      {     "scripts": {       "tsc": "tsc",       "tsc:w": "tsc -w"     },     "devDependencies": {       "@types/jasmine": "^2.5.35",       "jasmine-core": "^2.5.2",       "karma": "^1.3.0",       "karma-cli": "^1.0.1",       "karma-jasmine": "^1.0.2",       "karma-phantomjs-launcher": "^1.0.2",       "typescript": "^2.0.3"     }   }   

Create a tsconfig.json file. Since you're fine with the compiled files residing in the same directory, a simple one will do:

[tsconfig.json]      {     "compilerOptions": {       "target": "es5",       "module": "commonjs",       "moduleResolution": "node"     }   }   

Tip

You would probably not do it this way for a production application, but for a minimum viable setup, this will do in a pinch. A production application would most likely put compiled files into an entirely different directory, frequently named dist/.

Incorporating Webpack into Karma

Of course, you'll need a way of resolving module definitions for code and tests. Karma isn't capable of doing this on its own, so you'll need something to do this. Webpack is perfectly suitable for such a task, and Karma has a terrific plugin that allows you to preprocess your test files before they reach the browser.

Install Webpack and its Karma plugin:

 npm install webpack karma-webpack --save-dev 

Modify the Karma config to specify Webpack as the preprocessor. This allows your module definitions to be resolved properly:

[karma.conf.js]      module.exports = function(config) {     config.set({       browsers: [         'PhantomJS'       ],       frameworks: [         'jasmine'       ],       files: [         'src/*.spec.js'       ],       plugins : [         'karma-webpack',         'karma-jasmine',          'karma-phantomjs-launcher'       ],       preprocessors: {         'src/*.spec.js': ['webpack']       }     })   }   

Writing the test script

You can kick off the Karma server with the following:

 karma start karma.conf.js 

This will initialize the test server and run the tests, watching for changes and rerunning the tests. However, this sidesteps the fact that the TypeScript files require compilation in the files that Karma is watching. The TypeScript compiler also has a file watcher that will recompile on the fly. You would like both of these to recompile whenever you save changes to a source code file, so it makes sense to run them simultaneously. The concurrently package is suitable for this task.

Note

concurrently not only allows you to run multiple commands at once, but also to kill them all at once. Without it, a kill signal from the command line would only target whichever process was run most recently, ignoring the process that is running in the background.

Install concurrently with the following:

 npm install concurrently --save-dev 

Finally, build your test script to run Karma and the TypeScript compiler simultaneously:

[package.json]      {     "scripts": {       "test": "concurrently 'npm run tsc:w' 'karma start      karma.conf.js'",       "tsc": "tsc",       "tsc:w": "tsc -w"     },     "devDependencies": {       "@types/jasmine": "^2.5.35",       "concurrently": "^3.1.0",       "jasmine-core": "^2.5.2",       "karma": "^1.3.0",       "karma-cli": "^1.0.1",       "karma-jasmine": "^1.0.2",       "karma-phantomjs-launcher": "^1.0.2",       "karma-webpack": "^1.8.0",       "typescript": "^2.0.3",       "webpack": "^1.13.2"     }   }   

With this, you should be able to run your tests:

 npm test 

If everything is done correctly, the Karma server should boot up and run the tests, outputting the following:

 PhantomJS 2.1.1 (Linux 0.0.0): Executed 1 of 1 SUCCESS (0.038 secs / 0.001 secs) 

How it works...

Karma and Jasmine work together to deliver test files to the test browser. TypeScript and Webpack are tasked with converting your TypeScript files into a JavaScript format that will be usable by the test browser.

There's more...

An interesting consideration of this setup is how exactly TypeScript is handled.

Both the code and test files are written in TypeScript, which allows you to use the ES6 module notation, as opposed to some mix-and-match strategy. However, this leaves you with some choices to make on how the test setup should work.

The tests need to be able to use different pieces of your application in a piecemeal fashion, as opposed to the standard application setup where all the modules get pulled together at once. This recipe had TypeScript independently compile the .ts files, and it then directed Karma to watch the resultant .js files. This is perhaps easier to comprehend by someone who is easing into tests, but it might not be the most efficient way to go about it. Karma also supports TypeScript plugins, which allow you to preprocess the files into TypeScript before handing them off to the Webpack preprocessor.

Karma supports the chaining of preprocess steps, which will be useful if you want to compile the TypeScript on the fly as part of preprocessing.

See also

  • Writing a minimum viable unit test suite for a simple component shows you a basic example of unit testing Angular 2 components
  • Unit testing a synchronous service demonstrates how an injection is mocked in unit tests
  • Unit testing a component with a service dependency using Stubs shows how you can create a service mock to write unit tests and avoid direct dependencies
  • Unit testing a component with a service dependency using Spies shows how you can keep track of service method invocations inside a unit test
Назад: 9. Angular 2 Testing
Дальше: Writing a minimum viable unit test suite for a simple component

thank you
Flame
cant read the code since it is all on a single line. Also this comments section is russian
Rakuneque
DATA COLLECTION AND ANALYSIS Two reviewers extracted data and assessed methodological quality independently lasix torsemide conversion Many others were in that space already