亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

前端單元測試探索

陳江龍 / 1866人閱讀

摘要:單元測試的首要目的不是為了能夠編寫出大覆蓋率的全部通過的測試代碼,而是需要從使用者調(diào)用者的角度出發(fā),嘗試函數(shù)邏輯的各種可能性,進(jìn)而輔助性增強代碼質(zhì)量測試是手段而不是目的。

本文已發(fā)布在稀土掘金

轉(zhuǎn)載請注明原文鏈接:https://github.com/ecmadao/Co...

雖然很多公司有自己的測試部門,而且前端開發(fā)大多不涉及測試環(huán)節(jié),但鑒于目前前端領(lǐng)域的快速發(fā)展,其涉及面越來越廣,前端開發(fā)者們必然不能止步于目前的狀態(tài)。我覺得學(xué)會編寫良好的測試,不僅僅有利于自己整理需求、檢查代碼,更是一個優(yōu)秀開發(fā)者的體現(xiàn)。

首先不得不推薦兩篇文章:

前端自動化測試探索

測試驅(qū)動開發(fā)(TDD)介紹中的誤區(qū)

Intro

單元測試到底是什么?

需要訪問數(shù)據(jù)庫的測試不是單元測試

需要訪問網(wǎng)絡(luò)的測試不是單元測試

需要訪問文件系統(tǒng)的測試不是單元測試

--- 修改代碼的藝術(shù)

我們在單元測試中應(yīng)該避免什么?

太多的條件邏輯

構(gòu)造函數(shù)中做了太多事情

too many全局變量

too many靜態(tài)方法

無關(guān)邏輯

過多外部依賴

TDD(Test-driven development)

測試驅(qū)動開發(fā)(TDD),其基本思路是通過測試來推動整個開發(fā)的進(jìn)行。

單元測試的首要目的不是為了能夠編寫出大覆蓋率的全部通過的測試代碼,而是需要從使用者(調(diào)用者)的角度出發(fā),嘗試函數(shù)邏輯的各種可能性,進(jìn)而輔助性增強代碼質(zhì)量

測試是手段而不是目的。測試的主要目的不是證明代碼正確,而是幫助發(fā)現(xiàn)錯誤,包括低級的錯誤

測試要快??焖龠\行、快速編寫

測試代碼保持簡潔

不會忽略失敗的測試。一旦團隊開始接受1個測試的構(gòu)建失敗,那么他們漸漸地適應(yīng)2、3、4或者更多的失敗。在這種情況下,測試集就不再起作用

IMPORTANT

一定不能誤解了TDD的核心目的!

測試不是為了覆蓋率和正確率

而是作為實例,告訴開發(fā)人員要編寫什么代碼

紅燈(代碼還不完善,測試掛)-> 綠燈(編寫代碼,測試通過)-> 重構(gòu)(優(yōu)化代碼并保證測試通過)

大致過程

需求分析,思考實現(xiàn)??紤]如何“使用”產(chǎn)品代碼,是一個實例方法還是一個類方法,是從構(gòu)造函數(shù)傳參還是從方法調(diào)用傳參,方法的命名,返回值等。這時其實就是在做設(shè)計,而且設(shè)計以代碼來體現(xiàn)。此時測試為紅

實現(xiàn)代碼讓測試為綠

重構(gòu),然后重復(fù)測試

最終符合所有要求:

每個概念都被清晰的表達(dá)

Not Repeat Self

沒有多余的東西

通過測試

BDD(Behavior-driven development)

行為驅(qū)動開發(fā)(BDD),重點是通過與利益相關(guān)者的討論,取得對預(yù)期的軟件行為的清醒認(rèn)識,其重點在于溝通

大致過程

從業(yè)務(wù)的角度定義具體的,以及可衡量的目標(biāo)

找到一種可以達(dá)到設(shè)定目標(biāo)的、對業(yè)務(wù)最重要的那些功能的方法

然后像故事一樣描述出一個個具體可執(zhí)行的行為。其描述方法基于一些通用詞匯,這些詞匯具有準(zhǔn)確無誤的表達(dá)能力和一致的含義。例如,expect, should, assert

尋找合適語言及方法,對行為進(jìn)行實現(xiàn)

測試人員檢驗產(chǎn)品運行結(jié)果是否符合預(yù)期行為。最大程度的交付出符合用戶期望的產(chǎn)品,避免表達(dá)不一致帶來的問題

測試的分類 & 測試工具 分類

API/Func UnitTest

測試不常變化的函數(shù)邏輯

測試前后端API接口

UI UnitTest

頁面自動截圖

頁面DOM元素檢查

跑通交互流程

工具

Mocha + Chai

PhantomJS or CasperJS or Nightwatch.js

selenium

with python

with js

mocha + chai的API/Func UnitTest

mocha是一套前端測試工具,我們可以拿它和其他測試工具搭配。

而chai則是BDD/TDD測試斷言庫,提供諸如expect這樣的測試語法

initial

下面兩篇文章值得一看:

Testing in ES6 with Mocha and Babel 6

Using Babel

setup
$ npm i mocha --save-dev
$ npm i chai --save-dev
Use with es6

babel 6+

$ npm install --save-dev babel-register
$ npm install babel-preset-es2015 --save-dev
// package.json
{
  "scripts": {
    "test": "./node_modules/mocha/bin/mocha --compilers js:babel-register"
  },
  "babel": {
    "presets": [
      "es2015"
    ]
  }
}

babel 5+

$ npm install --save-dev babel-core
// package.json
{
  "scripts": {
    "test": "./node_modules/mocha/bin/mocha --compilers js:babel-core/register"
  }
}
Use with coffeescript
$ npm install --save coffee-script
{
  "scripts": {
    "test": "./node_modules/mocha/bin/mocha --compilers coffee:coffee-script/register"
  }
}
Use with es6+coffeescript

After done both...

{
  "scripts": {
    "test": "./node_modules/mocha/bin/mocha --compilers js:babel-core/register,coffee:coffee-script/register"
  }
}
# $ mocha
$ npm t
$ npm test
chai
import chai from "chai";

const assert = chai.assert;
const expect = chai.expect;
const should = chai.should();
foo.should.be.a("string");
foo.should.equal("bar");
list.should.have.length(3);
obj.should.have.property("name");

expect(foo).to.be.a("string");
expect(foo).to.equal("bar");
expect(list).to.have.length(3);
expect(obj).to.have.property("flavors");

assert.typeOf(foo, "string");
assert.equal(foo, "bar");
assert.lengthOf(list, 3);
assert.property(obj, "flavors");
Test

測試的一個基本思路是,自身從函數(shù)的調(diào)用者出發(fā),對函數(shù)進(jìn)行各種情況的調(diào)用,查看其容錯程度、返回結(jié)果是否符合預(yù)期。

import chai from "chai";
const assert = chai.assert;
const expect = chai.expect;
const should = chai.should();

describe("describe a test", () => {

  it("should return true", () => {
      let example = true;
      // expect
      expect(example).not.to.equal(false);
      expect(example).to.equal(true);
      // should
      example.should.equal(true);
      example.should.be.a(boolen);
      [1, 2].should.have.length(2);
  });
  
  it("should check an object", () => {
    // 對于多層嵌套的Object而言..
    let nestedObj = {
        a: {
          b: 1
      }
    };
    let nestedObjCopy = Object.assign({}, nestedObj);
    nestedObj.a.b = 2;
    
    // do a function to change nestedObjCopy.a.b 
    expect(nestedObjCopy).to.deep.equal(nestedObj);
    expect(nestedObjCopy).to.have.property("a");
  });
});
AsynTest

Testing Asynchronous Code with MochaJS and ES7 async/await

mocha無法自動監(jiān)聽異步方法的完成,需要我們在完成之后手動調(diào)用done()方法

而如果要在回調(diào)之后使用異步測試語句,則需要使用try/catch進(jìn)行捕獲。成功則done(),失敗則done(error)

// 普通的測試方法
it("should work", () =>{
  console.log("Synchronous test");
});
// 異步的測試方法
it("should work", (done) =>{
  setTimeout(() => {
    try {
        expect(1).not.to.equal(0);
        done(); // 成功
    } catch (err) {
        done(err); // 失敗
    }
  }, 200);
});

異步測試有兩種方法完結(jié):done或者返回Promise。而通過返回Promise,則不再需要編寫笨重的try/catch語句

it("Using a Promise that resolves successfully with wrong expectation!", function() {
    var testPromise = new Promise(function(resolve, reject) {
        setTimeout(function() {
            resolve("Hello World!");
        }, 200);
    });

    return testPromise.then(function(result){
        expect(result).to.equal("Hello!");
    });
});
mock

mock是一個接口模擬庫,我們可以通過它來模擬代碼中的一些異步操作

React單元測試 Test React Component

React組件無法直接通過上述方法進(jìn)行測試,需要安裝enzyme依賴。

$ npm i --save-dev enzyme
#
$ npm i --save-dev react-addons-test-utils

假設(shè)有這樣一個組件:

// ...省略部分import代碼
class TestComponent extends React.Component {
  constructor(props) {
    super(props);
    let {num} = props;
    this.state = {
      clickNum: num
    }
    this.handleClick = this.handleClick.bind(this)
  }

  handleClick() {
    let {clickNum} = this.state;
    this.setState({
      clickNum: clickNum + 1
    });
  }

  render() {
    let {clickNum} = this.state;
    return (
      
{clickNum} 點我加1
) } }

使用樣例:

import React from "react";
import {expect} from "chai";
import {shallow} from "enzyme";

import TestComponent from "../components/TestComponent";

describe("Test TestComponent", () => {
  // 創(chuàng)建一個虛擬的組件
  const wrapper = shallow(
      /
  );

  /* 
  * 之后,我們可以:
  * 通過wrapper.state()拿到組件的state
  * 通過wrapper.instance()拿到組件實例,以此調(diào)用組件內(nèi)的方法
  * 通過wrapper.find()找到組件內(nèi)的子組件
  * 但是,無法通過wrapper.props()拿到組件的props
  */

  // 測試該組件組外層的class
  it("should render with currect wrapper", () => {
    expect(wrapper.is(".test_component")).to.equal(true);
  });

  // 測試該組件初始化的state
  it("should render with currect state", () => {
    expect(wrapper.state()).to.deep.equal({
      clickNum: 10
    });
  });

  // 測試組件的方法
  it("should add one", () => {
    wrapper.instance().handleClick();
    expect(wrapper.state()).to.deep.equal({
      clickNum: 11
    });
  });
});
Test Redux

redux身為純函數(shù),非常便于mocha進(jìn)行測試

// 測試actions
import * as ACTIONS from "../redux/actions";

describe("test actions", () => {
  it("should return an action to create a todo", () => {
    let expectedAction = {
        type: ACTIONS.NEW_TODO,
        todo: "this is a new todo"
    };
     expect(ACTIONS.addNewTodo("this is a new todo")).to.deep.equal(expectedAction);
  });
});
// 測試reducer
import * as REDUCERS from "../redux/reducers";
import * as ACTIONS from "../redux/actions";

describe("todos", () => {
  let todos = [];
  it("should add a new todo", () => {
      todos.push({
        todo: "new todo",
        complete: false
    });
    expect(REDUCERS.todos(todos, {
        type: ACTIONS.NEW_TODO,
        todo: "new todo"
    })).to.deep.equal([
      {
          todo: "new todo",
          complete: false
      }
    ]);
  });
});
// 還可以和store混用
import { createStore, applyMiddleware, combineReducers } from "redux";
import thunk from "redux-thunk";
import chai from "chai";
import thunkMiddleware from "redux-thunk";
import * as REDUCERS from "../redux/reducers";
import defaultState from "../redux/ConstValues";
import * as ACTIONS from "../redux/actions"

const appReducers = combineReducers(REDUCERS);
const AppStore = createStore(appReducers, defaultState, applyMiddleware(thunk));
let state = Object.assign({}, AppStore.getState());

// 一旦注冊就會時刻監(jiān)聽state變化
const subscribeListener = (result, done) => {
  return AppStore.subscribe(() => {
    expect(AppStore.getState()).to.deep.equal(result);
    done();
  });
};

describe("use store in unittest", () => {
  it("should create a todo", (done) => {
    // 首先取得我們的期望值
      state.todos.append({
        todo: "new todo",
        complete: false
    });
    
    // 注冊state監(jiān)聽
    let unsubscribe = subscribeListener(state, done);
    AppStore.dispatch(ACTIONS.addNewTodo("new todo"));
    // 結(jié)束之后取消監(jiān)聽
    unsubscribe();
  });
});
基于phantomjs和selenium的UI UnitTest

PhantomJS是一個基于webkit的服務(wù)器端JavaScript API,即相當(dāng)于在內(nèi)存中跑了個無界面的webkit內(nèi)核的瀏覽器。通過它我們可以模擬頁面加載,并獲取到頁面上的DOM元素,進(jìn)行一系列的操作,以此來模擬UI測試。但缺點是無法實時看見頁面上的情況(不過可以截圖)。

Selenium是專門為Web應(yīng)用程序編寫的一個驗收測試工具,它直接運行在瀏覽器中。Selenium測試通常會調(diào)起一個可見的界面,但也可以通過設(shè)置,讓它以PhantomJS的形式進(jìn)行無界面的測試。

open 某個 url

監(jiān)聽 onload 事件

事件完成后調(diào)用 sendEvent 之類的 api 去點擊某個 DOM 元素所在 point

觸發(fā)交互

根據(jù) UI 交互情況 延時 setTimeout (規(guī)避惰加載組件點不到的情況)繼續(xù) sendEvent 之類的交互

Getting started with Selenium Webdriver for node.js

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/80389.html

相關(guān)文章

  • 程序員,軟件測試知多少?

    摘要:單元測試過后,機器狀態(tài)保持不變。單元測試應(yīng)該產(chǎn)生可重復(fù)一致的結(jié)果。然并卵都說國內(nèi)很多程序員是不寫單元測試的,甚至從來都不寫,筆者當(dāng)年做的時候也沒寫過幾次捂臉?;貧w測試在單元測試的基礎(chǔ)上,我們就能夠建立關(guān)于這一模塊的回歸測試。 showImg(https://segmentfault.com/img/bVPMPd?w=463&h=312); 送給初級程序員的測試認(rèn)知文 作為開發(fā)同學(xué),一些...

    libxd 評論0 收藏0
  • 軟件測試的概括及流程

    摘要:單元測試是在軟件開發(fā)過程中要進(jìn)行的最低級別的測試活動,軟件的獨立單元將在與程序的其他部分相隔離的情況下進(jìn)行測試。隨機測試隨機測試是根據(jù)測試說明書執(zhí)行用例測試的重要補充手段,是保證測試覆蓋完整性的有效方式和過程。 一、什么是軟件測試? ? ? ? ? 軟甲測試就是用來確認(rèn)一個程序的品質(zhì)或性能是...

    darryrzhong 評論0 收藏0
  • 數(shù)據(jù)驅(qū)動模式借助react的實踐探索

    摘要:之前談到過很多次數(shù)據(jù)驅(qū)動的理解,這次通過實際項目檢驗了一下自己的想法。對于數(shù)據(jù)驅(qū)動這種模式,至少從數(shù)據(jù)層,可以規(guī)避,做一層數(shù)據(jù)變化的效驗這個和寫服務(wù)端單側(cè)差不多。數(shù)據(jù)驅(qū)動和有點類似,只是借用在單頁面上實現(xiàn)了。 之前談到過很多次數(shù)據(jù)驅(qū)動的理解,這次通過實際項目檢驗了一下自己的想法。 相關(guān)文件 《前端數(shù)據(jù)驅(qū)動的價值》 《前端數(shù)據(jù)驅(qū)動的陷阱》 項目詳設(shè) 詳設(shè)的重要性 對于復(fù)雜一點的項目,...

    jone5679 評論0 收藏0
  • 關(guān)于前端接口測試探索和挖坑

    摘要:本文主要關(guān)注的是接口測試。所謂接口測試,就是檢查系統(tǒng)提供的接口是否符合事先撰寫的接口文檔。作為接口測試的解決方案,我們必須具備通用性與易用性。 開始 最近幾年,前端測試漸漸被人重視,相關(guān)的框架和方法已經(jīng)比較成熟。斷言庫有should, expect, chai。 單元測試框架有mocha, jasmine, Qunit。 模擬瀏覽器測試環(huán)境有Phantomjs, Slimerjs。 集...

    Crazy_Coder 評論0 收藏0
  • 關(guān)于前端接口測試探索和挖坑

    摘要:本文主要關(guān)注的是接口測試。所謂接口測試,就是檢查系統(tǒng)提供的接口是否符合事先撰寫的接口文檔。作為接口測試的解決方案,我們必須具備通用性與易用性。 開始 最近幾年,前端測試漸漸被人重視,相關(guān)的框架和方法已經(jīng)比較成熟。斷言庫有should, expect, chai。 單元測試框架有mocha, jasmine, Qunit。 模擬瀏覽器測試環(huán)境有Phantomjs, Slimerjs。 集...

    zxhaaa 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<