单元测试的工具们

  • 单元测试框架

    *   Qunit - jquery的单测工具
    
    • jasmine - 早期的测试框架
    • mocha - 常用框架 支持BDD和TDD
    • Cucumber - 语义化更好的测试工具
  • 断言

    *   chai
    
  • 集成

    *   Karma
    
    • jenkins
    • travis-ci
  • mock

    *   sinon
    
    • supertest
  • 组件测试框架

    *   polymer的单测工具:web-component-tester
    
    • react的单测工具:Jest

之前的博客对比较基础的测试工具都有所介绍,参考前端自动化测试基础篇,在这篇blog中,我们首先了解一下polymer组件的单元测试工具。

why web-component-tester

web-component-tester是polymer组件用于单元测试的框架,主要是用作对于html文件的测试

  • 它内部集成了mocha、sinon、sinon-chai、chai,方便使用
  • 使用lodash作为工具函数
  • async用作异步函数的测试
  • test-fixture作为
  • accessibility-developer-tools将测试结果输出到命令行中。
  • 在server端wct内置selenium-standalone,在客户端使用wd.js操作服务器进行测试

API方法

基本API

  • WCT.loadSuites可以将
  • suite类似describe将测试进行归类
  • test类似it进行具体的测试
  • assert用于进行断言,断言同chai的assert的断言规则

    suite(‘AwesomeLib’, function() {
    test(‘is awesome’, function() {

    assert.isTrue(AwesomeLib.awesome);//TDD模式
    

    });
    });
    `

    特殊的方法

  • text-fixture 用来在测试过程中操作template中的dom元素,用法是在

    `<test-fixture id="simple">
      <template>
        <div></div>
      </template>
    </test-fixture>
    <script>
      suite('classList', function() {
        var div;
        setup(function() {
          div = fixture('simple');
        })
        test('foo', function() {
          div.classList.add('foo');
          assertSomethingOrOther(div);
        });
      });
    </script>
    `

    关于template binding异步的测试

    异步测试在javascript的世界中可谓最常见,对于polymer组件来说,数据驱动模板刷新很重要,
    模板数据的刷新会调用Polymer.dom.flush, 它是个异步的过程,对此 web-components-tester 专门提供
    flush函数处理此类异步。

    `suite('with two selected items', function() {
      // Clean up after ourselves.
      teardown(function(done) {
        s.clearSelection();
        s.multi = false;
        // Wait for observers to resolve before moving on to more tests.
        flush(done);
      });
    
      test('multi selects by index', function(done) {
        s.multi = true;
        //数据变化
        s.selected = [0, 2];
        flush(function() {
          //模板刷新
          assert.equal(s.selectedIndex, [0, 2]);
          assert(s.children[0].classList.contains('core-selected'));
          assert(!s.children[1].classList.contains('core-selected'));
          assert(s.children[2].classList.contains('core-selected'));
          done();
        });
      });
    
    });
    `

    google map component测试实例

  • 代码参考,f2e test

  • 1、安装 web-components-tester: npm install -g web-component-tester
  • 2 建立test文件夹(默认地址)

    *   2.1 index.html
    
    • 2.2 google-map-marker.html
    • 2.3 marker.js
    `
    suite('markers default', function () {
        var map;
        setup(function () {
            map = document.querySelector('#map');
        });
    
        test('markers are initialized', function () {
            var markerEl = Polymer.dom(map).querySelector('google-map-marker');
            assert.isUndefined(markerEl.marker);
            assert.isUndefined(markerEl.map);
            assert.isNull(markerEl.info);
            assert.equal(markerEl.latitude, 37.779);
            assert.equal(markerEl.longitude, -122.3892);
        });
    
        test('markers are added to map', function () {
            map.addEventListener('google-map-ready', function () {
                var mapMarkerEl = Polymer.dom(map).querySelector('google-map-marker');
                var firstMarker = map.markers[0];
                expect(firstMarker).to.deep.equal(mapMarkerEl);
                assert.equal(map.markers.length, 3);
            });
        });
    
        test('markers position can be updated', function (done) {
            map.addEventListener('google-map-ready', function (e) {
                var markerEl = Polymer.dom(map).querySelector('google-map-marker');
                markerEl.latitude = 37.79493;
                markerEl.longitude = -122.41942;
                markerEl.zIndex = 1;
                assert.equal(markerEl.map, map.map, "marker's map is not the google-map's");
                //重新渲染 异步过程
                Polymer.dom.flush();
                async.nextTick(function () {
                    var marker = markerEl.marker;
                    assert.equal(marker.getPosition().lat(), markerEl.latitude);
                    assert.equal(marker.getPosition().lng(), markerEl.longitude);
                    assert.equal(marker.getZIndex(), markerEl.zIndex);
                    done();
                });
            });
        });
    });
    `
  • 3 运行测试脚本 wct 即可。

    wct运行机制

    代码:runner

    • 建立webserver,模板参见index.html
    • 读取wct.conf.json配置
    • 内置selenium server 通过wd.js建立连接打开浏览器进行测试
    • 并将结果通过socketIO返回显示在命令行
    • 测试框架核心:browser.js, 源码内置chai mocha sinon socket等以及polymer测试的辅助函数

    辅助工具

    在测试中少不了点击事件的模拟,wct这个工具不具有这个功能,但是可以使用polymer的工具组件iron-test-helpers
    它内置了MockInteraction可以实现各个事件的模拟,只需import iron-test-helpers.html 即可。使用方法如下:

    `test(‘can be triggered with space’, function(done) {
    button.addEventListener(‘keydown’, function() {

    done();
    

    });
    MockInteractions.pressSpace(button);
    });

    test(‘can be clicked’, function(done) {
    button.addEventListener(‘click’, function() {

    done();
    

    });
    MockInteractions.tap(button);
    });