• 使用karma和jasmine进行angularjs单元测试


    JavaScript是一门脚本语言,并不包含编译器,所以无法保证类型安全。

    单元测试可以弥补编译器的缺乏,找出潜在的缺陷。

    1 Jasmine测试框架

    Jasmine是一种测试框架,定义了测试用例的语法、API、如何编写断言等等。类似的产品还包括Mocha等。

    call browser-visit-direct.html  # windows
    open browser-visit-direct.html  # osx
    

    或者直接用浏览器打开browser-visit-direct.html:

    <html>
        <head>
            <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/jasmine/2.3.3/jasmine.min.css">
            <script src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/2.3.3/jasmine.min.js"></script>
            <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/2.3.3/jasmine-html.min.js"></script>
            <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/2.3.3/boot.min.js"></script>
        </head>
        <body>
        </body>
        <script type="text/javascript">
            var calculator = {
                sum: function(x, y) {
                    return x + y;
                }
            }
            // Paste in the test code here.
            describe('calculator', function () {
                it('1 + 1 should equal 2'); //没有添加测试的主体,因此状态上方显示的是pending
                it('1 + 1 should equal 2', function() {
                    expect(1 + 1).toBe(1);
                });
                it('1 + 1 should equal 2', function() {
                    expect(1 + 1).toBe(2);
                });
                it('1 + 1 should equal 2', function () {
                    expect(calculator.sum(1, 1)).toBe(2);
                });
            });
            
            it('others', function(){
              expect(true).toBe(true);
              expect(false).not.toBe(true);
              expect(1).toEqual(1);
              expect('foo').toEqual('foo');
              expect('foo').not.toEqual('bar');
            })
        </script>
    </html>
    

    可以看到

    failures

    Spec list

    其中,测试中的首句describe可以理解为包含多个测试样例的容器。

    it是单元测试的核心代码,我们在it块内调用函数,检查返回值是否符合预期。每个expect语句都接受一个参数,根据内置的匹配函数或者自定义匹配函数进行判断。

    常用的Jasmine匹配函数包括:

    1. toEqual
    2. toBe
    3. toBeTruthy
    4. toBeFalsy

    ……

    2 Karma安装和配置

    1、install nodejs: https://nodejs.org/en/

    2、淘宝cnpm:https://npm.taobao.org/

    查看全局安装的模块:cnpm list -g

    3、安装karma

    $ cnpm install -g karma-cli # 为了能在命令行直接执行 karma 命令
    $ mkdir ex03 & cd ex03
    $ cnpm install karma --save-dev  # 自动生成package.json
    $ cnpm install karma-jasmine karma-chrome-launcher --save-dev
    $ cat package.json                    
    {                                     
      "devDependencies": {                
        "karma": "^1.7.1",                
        "karma-chrome-launcher": "^2.2.0",
        "karma-jasmine": "^1.1.0"         
      }                                   
    }     
    
    $ karma init # 交互式自动生成karma.conf.js
    

    3 单元测试

    3.1 编写

    // File: ex03/controller.js                                           
    angular.module('notesApp', [])                                            
      .controller('ListCtrl', [function() {                                   
                                                                              
        var self = this;                                                      
        self.items = [                                                        
          {id: 1, label: 'First', done: true},                                
          {id: 2, label: 'Second', done: false}                               
        ];                                                                    
                                                                              
        self.getDoneClass = function(item) {                                  
          return {                                                            
            finished: item.done,                                              
            unfinished: !item.done                                            
          };                                                                  
        };                                                                    
    }]);                                                                      
    // File: ex03/controllerSpec.js                                       
    describe('Controller: ListCtrl', function() {                             
      // Instantiate a new version of my module before each test              
      beforeEach(module('notesApp'));                                         
                                                                              
      var ctrl;                                                               
                                                                              
      // Before each unit test, instantiate a new instance                    
      // of the controller                                                    
      beforeEach(inject(function($controller) {                               
        ctrl = $controller('ListCtrl');                                       
      }));                                                                    
                                                                              
      it('should have items available on load', function() {                  
        expect(ctrl.items).toEqual([                                          
          {id: 1, label: 'First', done: true},                                
          {id: 2, label: 'Second', done: false}                               
        ]);                                                                   
      });                                                                     
                                                                              
      it('should have highlight items based on state', function() {           
        var item = {id: 1, label: 'First', done: true};                       
                                                                              
        var actualClass = ctrl.getDoneClass(item);                            
        expect(actualClass.finished).toBeTruthy();                            
        expect(actualClass.unfinished).toBeFalsy();                           
                                                                              
        item.done = false;                                                    
        actualClass = ctrl.getDoneClass(item);                                
        expect(actualClass.finished).toBeFalsy();                             
        expect(actualClass.unfinished).toBeTruthy();                          
      });                                                                     
                                                                              
    });                                                                       
    

    karma.conf.js中,files包含需要引入的js文件,browser表示要启动的浏览器

        // list of files / patterns to load in the browser
       files: [
          'angular.min.js',
          'angular-mocks.js',
          'controller.js',
          'controllerSpec.js'
        ],
        // Start these browsers, currently available:
        // - Chrome
        // - ChromeCanary
        // - Firefox
        // - Opera
        // - Safari (only Mac)
        // - PhantomJS
        // - IE (only Windows)
        browsers: ['Chrome'],
    

    3.2 运行

    $ karma start
    11 11 2017 10:29:41.138:WARN [karma]: No captured browser, open http://localhost:8080/
    11 11 2017 10:29:41.152:INFO [karma]: Karma v1.7.1 server started at http://0.0.0.0:8080/
    11 11 2017 10:29:41.153:INFO [launcher]: Launching browser Chrome with unlimited concurrency
    11 11 2017 10:29:41.166:INFO [launcher]: Starting browser Chrome
    11 11 2017 10:29:43.146:INFO [Chrome 62.0.3202 (Windows 10 0.0.0)]: Connected on socket Yi2gR771fEmcfPqnAAAA with id 31405350
    Chrome 62.0.3202 (Windows 10 0.0.0): Executed 2 of 2 SUCCESS (0.032 secs / 0.022 secs)
    
    

    源码:https://github.com/shenjiefeng/js-fortest/tree/master/angularjs/ex04-unittest/

    3.3 使用PhantomJS

    phantomjs是提供一个浏览器环境的命令行接口

    $ cnpm install --save-dev karma-phantomjs-launcher
    

    参考官网 https://www.npmjs.com/package/karma-phantomjs-launcher ,修改karma.conf.js

    运行

    $ karma start --browsers PhantomJS_custom
    11 11 2017 11:10:29.055:WARN [karma]: No captured browser, open http://localhost:8080/
    11 11 2017 11:10:29.071:INFO [karma]: Karma v1.7.1 server started at http://0.0.0.0:8080/
    11 11 2017 11:10:29.071:INFO [launcher]: Launching browser PhantomJS_custom with unlimited concurrency
    11 11 2017 11:10:29.086:INFO [launcher]: Starting browser PhantomJS
    11 11 2017 11:10:29.133:INFO [phantomjs.launcher]: ACTION REQUIRED:
    11 11 2017 11:10:29.133:INFO [phantomjs.launcher]:
    11 11 2017 11:10:29.133:INFO [phantomjs.launcher]:   Launch browser at
    11 11 2017 11:10:29.133:INFO [phantomjs.launcher]:   http://localhost:9000/webkit/inspector/inspector.html?page=2
    11 11 2017 11:10:29.133:INFO [phantomjs.launcher]:
    11 11 2017 11:10:29.133:INFO [phantomjs.launcher]: Waiting 15 seconds ...
    11 11 2017 11:10:47.356:INFO [PhantomJS 2.1.1 (Windows 8 0.0.0)]: Connected on socket gYJpL9b4Ax1vcSXRAAAA with id 54603354
    PhantomJS 2.1.1 (Windows 8 0.0.0): Executed 2 of 2 SUCCESS (0 secs / 0.017 secs)
    

    3.4 代码覆盖率

    代码覆盖率常常被拿来作为衡量测试好坏的指标

    cnpm install karma karma-coverage --save-dev
    

    参照官网 https://www.npmjs.com/package/karma-coverage 修改karma.conf.js

        // coverage settings
        // coverage reporter generates the coverage
        reporters: ['progress', 'coverage'],
    
        preprocessors: {
          // source files, that you wanna generate coverage for
          // do not include tests or libraries
          // (these files will be instrumented by Istanbul)
          'src/**/*.js': ['coverage']
        },
    
        // optionally, configure the reporter
        coverageReporter: {
          type : 'html',
          dir : 'coverage/'
        },
        // end coverage settings done
    

    运行

    λ karma start                                                                           
    11 11 2017 15:44:39.502:WARN [karma]: No captured browser, open http://localhost:8080/  
    11 11 2017 15:44:39.502:INFO [karma]: Karma v1.7.1 server started at http://0.0.0.0:8080
    /                                                                                       
    11 11 2017 15:44:39.518:INFO [launcher]: Launching browsers PhantomJS, PhantomJS_custom 
    with unlimited concurrency                                                              
    11 11 2017 15:44:39.518:INFO [launcher]: Starting browser PhantomJS                     
    11 11 2017 15:44:39.565:INFO [launcher]: Starting browser PhantomJS                     
    11 11 2017 15:44:39.580:INFO [phantomjs.launcher]: ACTION REQUIRED:                     
    11 11 2017 15:44:39.580:INFO [phantomjs.launcher]:                                      
    11 11 2017 15:44:39.580:INFO [phantomjs.launcher]:   Launch browser at                  
    11 11 2017 15:44:39.580:INFO [phantomjs.launcher]:   http://localhost:9000/webkit/inspec
    tor/inspector.html?page=2                                                               
    11 11 2017 15:44:39.580:INFO [phantomjs.launcher]:                                      
    11 11 2017 15:44:39.580:INFO [phantomjs.launcher]: Waiting 15 seconds ...               
    11 11 2017 15:44:42.665:INFO [PhantomJS 2.1.1 (Windows 8 0.0.0)]: Connected on socket tR
    S1I1S_vRFKRXcwAAAA with id 93001704                                                     
    11 11 2017 15:44:57.665:INFO [PhantomJS 2.1.1 (Windows 8 0.0.0)]: Connected on socket dk
    GUuJAC80MqA_98AAAB with id 67337991                                                     
    PhantomJS 2.1.1 (Windows 8 0.0.0): Executed 2 of 2 SUCCESS (0.016 secs / 0.01 secs)     
    PhantomJS 2.1.1 (Windows 8 0.0.0): Executed 2 of 2 SUCCESS (0 secs / 0.012 secs)        
    TOTAL: 4 SUCCESS                                                                        
    

    查看生成的代码覆盖率

    $ ls "coveragePhantomJS 2.1.1 (Windows 8 0.0.0)
    base.css  index.html  prettify.css  prettify.js  sort-arrow-sprite.png  sorter.js
    

    100 code coverage

    4 在测试中注入html,访问DOM元素

    4.1 将html写入到js中

    源码:https://github.com/shenjiefeng/js-fortest/tree/master/angularjs/ex02-karma-tutorial

    4.2 从外部html文件导入

    将html写入到js中太麻烦了,可不可以用karma-fixture来实现导入呢

     karma start
    11 11 2017 16:12:49.337:WARN [karma]: No captured browser, open http://localhost:9876/
    11 11 2017 16:12:49.353:INFO [karma]: Karma v1.7.1 server started at http://0.0.0.0:9876/
    11 11 2017 16:12:49.353:INFO [launcher]: Launching browsers PhantomJS, PhantomJS_custom with unlimited concurrency
    11 11 2017 16:12:49.368:INFO [launcher]: Starting browser PhantomJS
    11 11 2017 16:12:49.399:INFO [launcher]: Starting browser PhantomJS
    11 11 2017 16:12:49.415:INFO [phantomjs.launcher]: ACTION REQUIRED:
    11 11 2017 16:12:49.415:INFO [phantomjs.launcher]:
    11 11 2017 16:12:49.415:INFO [phantomjs.launcher]:   Launch browser at
    11 11 2017 16:12:49.415:INFO [phantomjs.launcher]:   http://localhost:9000/webkit/inspector/inspector.html?page=2
    11 11 2017 16:12:49.415:INFO [phantomjs.launcher]:
    11 11 2017 16:12:49.415:INFO [phantomjs.launcher]: Waiting 15 seconds ...
    11 11 2017 16:12:52.545:INFO [PhantomJS 2.1.1 (Windows 8 0.0.0)]: Connected on socket ddCmyvggaUdxkr1rAAAA with id 6133081
    11 11 2017 16:13:07.557:INFO [PhantomJS 2.1.1 (Windows 8 0.0.0)]: Connected on socket C670QlbDCLLzPfHYAAAB with id 60711068
    LOG: Fixture{base: 'test', id: 'fixture_container', json: [], scriptTypes: Object{application/ecmascript: 1, application/javascript: 1, application/x-ecmascript: 1, application/x-javascript: 1, text/ecmascript: 1, text/javascript: 1, text/javascript1.0: 1, text/javascript1.1: 1, text/javascript1.2: 1, text/javascript1.3: 1, text/javascript1.4: 1, text/javascript1.5: 1, text/jscript: 1, text/livescript: 1, text/x-ecmascript: 1, text/x-javascript: 1}, el: <div id="fixture_container"></div>}
    PhantomJS 2.1.1 (Windows 8 0.0.0) Calculator should return 3 for 1 + 2 FAILED
            ReferenceError: Cannot find fixture 'test/hello.html' in node_modules/_karma-fixture@0.2.6@karma-fixture/lib/fixture.js (line 141)
            _throwNoFixture@node_modules/_karma-fixture@0.2.6@karma-fixture/lib/fixture.js:141:77
            load@node_modules/_karma-fixture@0.2.6@karma-fixture/lib/fixture.js:78:33
            test/calculator.test.js:49:24
            executeFiltered@node_modules/_karma-jasmine@0.2.3@karma-jasmine/lib/boot.js:126:18
            node_modules/_karma-jasmine@0.2.3@karma-jasmine/lib/adapter.js:171:31
            loaded@http://localhost:9876/context.js:162:17
            global code@http://localhost:9876/context.html:48:28
            TypeError: null is not an object (evaluating 'document.getElementById('x').value = 1') in test/calculator.test.js (line 60)
            test/calculator.test.js:60:33
            executeFiltered@node_modules/_karma-jasmine@0.2.3@karma-jasmine/lib/boot.js:126:18
            node_modules/_karma-jasmine@0.2.3@karma-jasmine/lib/adapter.js:171:31
            loaded@http://localhost:9876/context.js:162:17
            global code@http://localhost:9876/context.html:48:28
    

    对应的代码覆盖率:

    error code coverage

    error code coverage
    可以看出,9句中4句未覆盖。

    如何解决ReferenceError: Cannot find fixture 'test/hello.html'问题呢?

    几经尝试,还是同样的问题,忧桑啊。

    参考

    1. 《AngularJS即学即用》
    2. https://npmjs.org/browse/keyword/karma-launcher
    3. https://segmentfault.com/a/1190000005708178
    4. Karma Tutorial - Unit Testing JavaScript
  • 相关阅读:
    详解vue生命周期
    浅谈前端中的mvvm与mvc
    实际项目开发需要注意的tips
    toFixed()一不小心踩了一个坑
    git学习(持续踩坑中🤣)
    webpack基础
    创建git仓库并发布
    注册npm账号
    Invalid left-hand side in assignment
    关于项目中js原型的使用
  • 原文地址:https://www.cnblogs.com/lawlietfans/p/7819527.html
Copyright © 2020-2023  润新知