ANGULARJS unit tests based on Karma and Jasmine
Directory:
1. Configuration of unit tests
2. Example File directory interpretation
3. Test controller
3.1 Test the variable value in the controller is correct
3.2 Analog HTTP request return value, test $http service related
4. Read JSON from a file to simulate HTTP request return data
5. Test the service returned to promise
There have been many tutorials that mention unit tests for the ANGULARJS project, but most of them are not very full, such as some introductory articles that describe the test HTTP service but do not describe how to read the test data from the file to simulate. Some articles that describe how to read simulation data from a file are too deep for getting started. So I wrote this tutorial about the situation that I often encounter at work. Hope a little use:)
1. Configuration of unit tests
- installation Angular
npm Install angular--save
- installation Karma
npm install-g Karma--save-dev
- installation Jasmine
npm Install karma-jasmine jasmine-core--save-dev
- installation Ngmock
npm Install angular-mocks--save-dev
- installation jasmine-jquery
Bower Install Jasmine-jquery--save
- installation Karma-read-json
Bower Install Karma-read-json
- Download example
Https://github.com/wuhaibo/angularUnitTest
2. Example File directory interpretation
3. Test Controller
First look at the Code of our controller
1 ' use strict '; 2/* Controllers */3 */module */4 var Unittestapp = angular.module (' Unittestapp ', []); 5 6/* Controllers */7 Unittestapp.controller (' Unittestctrl ', function ($scope, $http) {8 9 //set name Ten $s Cope.name = "William Wood"; //Get User13 $scope via HTTP request . GetUser = function () { $http. Get ('/auth.py '). Then (function (response) { $scope. user = Response.data;16 }); };18});
This controller is simple, with two elements
- A variable name is declared in scope and assigned a value of williamwood
- A function GetUser is defined, which sends an HTTP GET request to assign a value to Scope.user
Let's test 1 and test 2 first.
3.1 Test The variable value in the controller is correct
The code tested in /test/unit/controllersspec. js, The test code is simply described below
1 ' use strict '; 2 3//test type description, here means test Unittestapp Controllers 4 describe (' Unittestapp controllers ', function () {5 6//test type description , which indicates that the test Unittestctrl this controller 7 describe (' Unittestctrl ', function () {8 9//beforeeach represents the preparation before running all tests. 10//This generates UNITTESTAPP MODULE11 Beforeeach (module (' Unittestapp ')), 12 13//Defines the object that will be used in the test in order to use 14 in the entire test environment var scope,ctrl;15//inject takes advantage of angular's dependency injection, which will require the module to be inserted into the scope of the service (inject (function ($controller, $root Scope) {18//simulation generates scope, $rootScope is the//scope in the top scope,angular in angular is rootscope new The scope = $rootScope. $new (); 21//simulates generating a controller and passing in the previously generated scope to facilitate testing with CTRL = $controller (' Unitte Stctrl ', {$scope: scope}); 24 25//test starting from here//it ' should create name William Wood in UnitTest Ctrl ' Description of the project tested by it (' should create name William Wood in Unittestctrl ', inject (function () {29//test expected S The value of the Cope.nameFor William Wood expect (scope.name) toequal (' William Wood '); 33//Test GetUser function, detailed below 34 It (' GetUser should fetch users ', inject (function ($injector) {35 .... ()); );
use describe to describe the testcase category in Jasmine (test which controller, which modular...). Beforeeach is used for pre-test preparation, inject takesadvantage of angular 's dependency injection, inserts the required modules , and services into the scope. The real test code in the it function, the first parameter of this function is testcase description , the second function is the test logic .
Test configuration (You can skip this step)
The test can be configured with the Karma Init command, which generates a karma.conf.js file to be used as a test configuration file. This step can be skipped because the file is already in the instance folder. You can use the instance file structure later as the base template for other projects.
Run Tests
1. Windows commandline enters the directory where karma.conf.js resides.
2.Run instructionsKarma Start, This will pop up the browser window, no tube, they are launched to perform the test, let them stay in the background can be. karma Test success as shown here, because there are two test cases in the test file, you can see executed 1 of 2 ... ( for testing convenience, firefox Test platform is commented out, All tests will only run on chromefirefoxkarma.conf.js browsers: [' Chrome '/*, ' Firefox ' */] browsers: [' Chrome ', ' Firefox ']
3. Test failure condition
ModifyExpect(Scope.Name).Toequal ( ' William Wood ' ) < Span lang= "en-US" > expect (scope< Span lang= "en-us". name). toequal ( "William Wood is me< Span lang= "en-us", " , found that the test ran automatically, and there was an error report.
Ok, that's it . We can already test a controller . Below we describe how to simulate the return value of an HTTP request to test $http service-related logic.
3.2 Analog HTTP request return value, Test http service related
Remember we have a GetUser function in the controller
1//Get User2 $scope via HTTP request . GetUser = function () {3 $http. Get ('/auth.py '). Then (function (response) {4 $scope. user = Response.data;5 } );
This function obtains the user's value through an HTTP GET request .
In the unit test we don't really want to send an http get request to run the test because that complicates the test, the network-related problems will cause the test to fail, and the angular HTTP service is asynchronous, And we want the tests to be synchronous. So how do you do it?
First look at the code of the test , still in /test/unit/controllersspec.js
Simulating the return value of an HTTP GET, inserting the injector service, allows us to use dependency injection in the test code to get the service it needs (' GetUser should fetch users ', inject (function ($ INJECTOR) { //$httpBackend is a mock HTTP request provided by the angular mock return service //can be used to simulate the return value of an HTTP request// here to get an instance of it through $injector var $httpBackend = $injector. Get (' $httpBackend '); $httpBackend in the Get method, the URL to '/auth.py ' will return a Jason object //{customerId: ' 1 ', Name: ' Benwei '} $httpBackend. When (' GET ', '/auth.py '). Respond ({customerId: ' 1 ', Name: ' Benwei '}); The above is the pre-test preparation, you can also put this part of the code in Beforeeach, //But note: The settings in Beforeeach will affect all the test cases at its scope. //Run the GetUser function scope. GetUser (); The asynchronous conversion of HTTP to synchronous requires $httpbackend to return data $httpBackend immediately. Flush (); See if the value of Scope.user is correct expect (Scope.user). Toequal ({customerId: ' 1 ', Name: ' Benwei '})) ;
4. Read json from a fileto simulate HTTP request return Data
There are times when we need to return larger JSON data , where JSON data is not as realistic as it would be in the test code. The more feasible scenario is to Save the JSON data in a JSON file and read the data from the file. At this point we need Karma-read-json help.
We have installed this plugin in the configuration of the unit test and set it in/test/karma.conf.js, which is a simple description of the settings. (You can skip reading this step, just remember to place the JSON file used for impersonation in the test/mock/folder, and the file suffix is. json)
1. introduction of the Karma-read-json framework in the test
Files: [... Test Framework ' app/bower_components/karma-read-json/karma-read-json.js ', ... ],
2. Specify the analog data file format that will be used in the test for karma.
Files: [... Fixtures {pattern: ' Test/mock/*.json ', included:false}, ... ],
Note that the root directory here is set in the Karma.conf.js file, as follows
BasePath: '. /',//Set the directory where Karma.conf.js/... / is the root directory
JSON files needed to simulate data in this instance should be placed in the test/mock folder
when the setup is finished, check out our test code
It (' GetUser should fetch users mock response from file ', inject (function ($injector) { //read analog return data from file var Valid_respond = Readjson (' Test/mock/data.json '); Here we get an instance of the Httpbackend service by $injector . var $httpBackend = $injector. Get (' $httpBackend '); $httpBackend in the GET method, the URL to '/auth.py ' will return //A JSON object $httpBackend read from Test/mock/data.json . When (' GET ', '/ Auth.py '). Respond (Valid_respond); $httpBackend in the Get method, the URL to '/auth.py ' will return a Jason object //{customerId: ' 1 ', Name: ' Benwei '} $httpBackend. When (' GET ', '/auth.py '). Respond ({customerId: ' 1 ', Name: ' Benwei '}); Run the GetUser function scope. GetUser (); The asynchronous conversion of HTTP to synchronous requires $httpbackend to return data $httpBackend immediately. Flush (); See if the value of Scope.user is correct expect (scope.user.length). ToBe (2); });
5. Test The service returned to promise
First look at the service code, the code can be found in the App\js\services.js
' Use strict '; /* Services */unittestapp.factory (' getusernumberservice ', function ($http, $q) { var deferred = $q. Defer (); The HTTP Service Request $http ({method: ' GET ', url: '/auth.py '}). Then ( function (response) { Deferred.resolve ( response.data.length); }, function (response) { deferred.reject (response); } ); Return HTTP Service Request promise return deferred.promise; });
We created a service called Getusernumberservice , which gets the length of the returned data by sending an HTTP request. The test code for this service is as follows, and the code can be found in test\unit\servicesspec.js
"Use strict"; /* Jasmine specs for services go here */describe (' servicetest ', function () {Describe (' Test getuse Rnumberservice ', function () { //mock module Beforeeach (module (' Unittestapp ')); It (' Getusernumberservice should return 2 ', inject (function ($injector) { //Analog return data var Valid_r Espond = ' [{' customerId ': ' 1 ', ' name ': ' Benwei '},{' customerId ': ' 2 ', ' name ': ' William '}] '; var $httpBackend = $injector. Get (' $httpBackend '); $httpBackend. Whenget ('/auth.py '). Respond (Valid_respond); Get the service through injector, as in the previous example get $httpbackend like var getusernumberservice = $injector. Get (' Getusernumberservi Ce '); var promise = Getusernumberservice; var usernum; Promise.then (function (data) {usernum = data; }); Force Httpbackend to return data $httpBackend. Flush (); Get $rootscope var $rootScope by injector = $injector. Get(' $rootScope '); Forced to pass to the current scope $rootScope. $apply (); Test to determine if Usernum is 2 expect (usernum). Toequal (2); }); });
There is a notable place to use $rootScopein order to pass changes to the current scope. $apply();
ANGULARJS unit testing based on karma and Jasmine