When the ng project becomes larger and larger, unit testing has to be put on the agenda. Sometimes the team takes testing first, some implement functions first, and then tests functional modules. This has its own advantages and disadvantages. Today, we mainly talk about using karma and jasmine to perform unit testing of ng modules.
What is Karma
karma is a unit test run control framework, providing running unit tests in different environments, such as chrome, firfox, phantomjs, etc. The test framework supports jasmine, mocha, qunit, and is an npm module with nodejs as the environment.
It is recommended to use the ----save-dev parameter when installing and testing related npm modules, because this is related to development. Generally, if you run karma, only the following two npm commands are required.
The code copy is as follows:
npm install karma --save-dev
npm install karma-junit-reporter --save-dev
When installing karma, some commonly used modules will be automatically installed. Refer to the peerDependencies property of the package.json file in the karma code.
The code copy is as follows:
"peerDependencies": {
"karma-jasmine": "~0.1.0",
"karma-requirejs": "~0.2.0",
"karma-coffee-preprocessor": "~0.1.0",
"karma-html2js-preprocessor": "~0.1.0",
"karma-chrome-launcher": "~0.1.0",
"karma-firefox-launcher": "~0.1.0",
"karma-phantomjs-launcher": "~0.1.0",
"karma-script-launcher": "~0.1.0"
}
Then a typical running framework usually requires a configuration file. In karma, it can be a karma.conf.js. The code inside is a nodejs style. A common example is as follows:
The code copy is as follows:
module.exports = function(config){
config.set({
// The basic directory in the following files
basePath : '../',
// JS information that needs to be loaded in the test environment
files : [
'app/bower_components/angular/angular.js',
'app/bower_components/angular-route/angular-route.js',
'app/bower_components/angular-mocks/angular-mocks.js',
'app/js/**/*.js',
'test/unit/**/*.js'
],
// Whether to automatically listen to changes in the above file, automatically run the test
autoWatch : true,
// Application test framework
frameworks: ['jasmine'],
// What environment to use to test the code? Here is Chrome`
browsers: ['Chrome'],
// Plugins used, such as Chrome browser and jasmine plug-ins
plugins: [
'karma-chrome-launcher',
'karma-firefox-launcher',
'karma-jasmine',
'karma-junit-reporter'
],
// The output of the test content and the module name used to export
reporters: ['progress', 'junit'],
// Set the information of the output test content file
junitReporter : {
outputFile: 'test_out/unit.xml',
suite: 'unit'
}
});
};
When you need to pay attention to this, most of the above plug-ins do not need to be installed separately, because they have been installed when installing karma. Only the karma-junit-reporter export plug-ins need to be installed separately. If you want to know more about configuration files, click here
That’s all about karma. If you want to know more about it, click here
What is jasmine
Jasmine is a behavior-driven development framework for testing JavaScript code. It does not depend on any other JavaScript frameworks. It does not require a DOM. And it has a clean, obvious syntax so that you can easily write tests.
The above is the explanation of it in the official document of jasmine, and the following is a simple translation in Chinese
Jasmine is a behavior-driven testing framework that does not rely on any js framework and dom. It is a very clean and friendly API test library.
Here is a simple example to illustrate its usage
Define a test file command as test.js
The code copy is as follows:
describe("A spec (with setup and tear-down)", function() {
var foo;
beforeEach(function() {
foo = 0;
foo += 1;
});
afterEach(function() {
foo = 0;
});
it("is just a function, so it can contain any code", function() {
expect(foo).toEqual(1);
});
it("can have more than one expectation", function() {
expect(foo).toEqual(1);
expect(true).toEqual(true);
});
});
The above example comes from the official website. Here are only a few important APIs. For more usage, please click here
1. First, any test case is defined by the describe function. It has two parameters. The first is used to describe the overall central content of the test, and the second parameter is a function, which writes some real test code.
2.it is used to define a single specific test task, and it also has two parameters. The first one is used to describe the test content, and the second parameter is a function, which stores some test methods.
3. Expect is mainly used to calculate the value of a variable or an expression, and then compare it with the expected value or do some other events.
4.beforeEach and afterEach are mainly used to do some things before and after the test task is executed. The above example is to change the value of the variable before execution, and then reset the value of the variable after the execution is completed.
Finally, the scope in the describe function can be accessed in the subfunctions in it, just like the above it accesses the foo variable
If you want to run the above test example, you can run it through karar. The command example is as follows:
The code copy is as follows:
karma start test/karma.conf.js
Next, we will focus on unit testing of controllers, instructions, and service modules in ng.
Unit tests for NG
Because of the framework of ng itself, modules are loaded and instantiated through di, so in order to facilitate the writing of test scripts with jasmine, the official provides an angular-mock.js test tool class to provide module definition, loading, injection, etc.
Let’s talk about some common methods in ng-mock
1.angular.mock.module This method is also in the window namespace, which is very convenient to call.
module is used to configure the module information injected by the inject method. The parameters can be strings, functions, and objects. They can be used like the following.
The code copy is as follows:
beforeEach(module('myApp.filters'));
beforeEach(module(function($provide) {
$provide.value('version', 'TEST_VER');
}));
It is generally used in the beforeEach method, because this can ensure that the inject method can obtain the module configuration when executing the test task.
1.angular.mock.inject This method is also in the window namespace, which is very convenient to call.
Inject is used to inject the ng module configured above. It is called in the test function of it. Common calling examples are as follows:
The code copy is as follows:
angular.module('myApplicationModule', [])
.value('mode', 'app')
.value('version', 'v1.0.1');
describe('MyApp', function() {
// You need to load modules that you want to test,
// it loads only the "ng" module by default.
beforeEach(module('myApplicationModule'));
// inject() is used to inject arguments of all given functions
it('should provide a version', inject(function(mode, version) {
expect(version).toEqual('v1.0.1');
expect(mode).toEqual('app');
}));
// The inject and module method can also be used inside of the it or beforeEach
it('should override a version and test the new version is injected', function() {
// module() takes functions or strings (module aliases)
module(function($provide) {
$provide.value('version', 'overridden'); // override version here
});
inject(function(version) {
expect(version).toEqual('overridden');
});
});
});
The above are some inject examples provided by the official, and the code is easy to understand. In fact, inject is a built-in dependency injection instance created using the angular.inject method. Then the module injection is the same as the dependency processing in ordinary ng modules.
After briefly introducing ng-mock, we will write a simple unit test using controllers, instructions, and filters.
Unit test of controller
Define a simple controller
The code copy is as follows:
var myApp = angular.module('myApp',[]);
myApp.controller('MyController', function($scope) {
$scope.spices = [{"name":"pasilla", "spiciness":"mild"},
{"name":"jalapeno", "spiciness":"hot hot hot!"},
{"name":"habanero", "spiciness":"LAVA HOT!!"}];
$scope.spice = "hello feenan!";
});
Then we write a test script
The code copy is as follows:
describe('myController function', function() {
describe('myController', function() {
var $scope;
beforeEach(module('myApp'));
beforeEach(inject(function($rootScope, $controller) {
$scope = $rootScope.$new();
$controller('MyController', {$scope: $scope});
}));
it('should create "spices" model with 3 spices', function() {
expect($scope.spices.length).toBe(3);
});
it('should set the default value of spice', function() {
expect($scope.spice).toBe('hello feedback!');
});
});
});
The above uses $rootScope to create a subscope, and then passes this parameter into the controller's construction method $controller. Finally, the above method will be executed. Then we check whether the number of arrays in the subscope and whether the string variables are equal to the expected value.
If you want to know more about the controller in ng, you can click here
Unit test of instructions in ng
Define a simple instruction
The code copy is as follows:
var app = angular.module('myApp', []);
app.directive('aGreatEye', function () {
return {
restrict: 'E',
replace: true,
template: '<h1>lidless, lost in flame, 1 times</h1>'
};
});
Then we write a simple test script
The code copy is as follows:
describe('Unit testing great quotes', function() {
var $compile;
var $rootScope;
// Load the myApp module, which contains the directive
beforeEach(module('myApp'));
// Store references to $rootScope and $compile
// so they are available to all tests in this describe block
beforeEach(inject(function(_$compile_, _$rootScope_){
// The injector unwraps the underscores (_) from around the parameter names when matching
$compile = _$compile_;
$rootScope = _$rootScope_;
}));
it('Replaces the element with the appropriate content', function() {
// Compile a piece of HTML containing the directive
var element = $compile("<a-great-eye></a-great-eye>")($rootScope);
// fire all the watches, so the scope expression 1 will be evaluated
$rootScope.$digest();
// Check that the compiled element contains the templated content
expect(element.html()).toContain("lidless, lost in flame, 2 times");
});
});
The above example comes from the official one, and the above command will be used in html.
The code copy is as follows:
<a-great-eye></a-great-eye>
The test script first injects two services $compile and $rootScope, one is used to compile html and the other is used to create scope. Note that the _ is used here. When the service injected in ng is added before and after, it will be processed by ng. These two services are stored in two internal variables, so that the following test cases can be called.
The $compile method is passed into the original instruction html, and then the returned function is passed in $rootScope, which completes the binding of scope and view. Finally, call $rootScope.$digest to trigger all listening, ensuring that the model content in the view is updated
Then get the html content of the corresponding element of the current instruction and compare it with the expected value.
If you want to know more about the instructions in ng, you can click here
Filter unit test in ng
Define a simple filter
The code copy is as follows:
var app = angular.module('myApp', []);
app.filter('interpolate', ['version', function(version) {
return function(text) {
return String(text).replace(//%VERSION/%/mg, version);
};
}]);
Then write a simple test script
The code copy is as follows:
describe('filter', function() {
beforeEach(module('myApp'));
describe('interpolate', function() {
beforeEach(module(function($provide) {
$provide.value('version', 'TEST_VER');
}));
it('should replace VERSION', inject(function(interpolateFilter) {
expect(interpolateFilter('before %VERSION% after')).toEqual('before TEST_VER after');
}));
});
});
The above code first configures the filter module and then defines a version value. Because interpolate depends on this service, finally injects the interpolate filter with inject. Note that the filter here must be followed by the Filter suffix, and finally passes in the text content into the filter function to execute, and compares it with the expected value.
Summarize
There are many benefits to using tests to develop NG, which can ensure the stability of the module. Another thing is that you can have an in-depth understanding of the internal operating mechanism of NG, so it is recommended that students who use NG to develop should fill in the tests as soon as possible!