測試框架Jest

本文全部資料請參考:github.com/antgod/reac…html

測試框架Jest

1. 快速開始

安裝jest

npm install --save-dev jest複製代碼

咱們先寫一個測試函數,有兩個數字參數作加法,首先,建立sum.js文件node

function sum(a, b) {
  return a + b
}
module.exports = sum複製代碼

而後,建立建立sum.test.js,包含咱們目前的測試代碼。react

const sum = require('./sum');

test('adds 1 + 2 to equal 3', () => {
  expect(sum(1, 2)).toBe(3);
});複製代碼

而後添加片斷到package.json中android

{
  "scripts": {
    "test": "jest"
  }
}複製代碼

最後運行npm test,jest輸入以下內容webpack

PASS  ./sum.test.js
✓ adds 1 + 2 to equal 3 (5ms)複製代碼

你剛剛成功的編寫了一個jest測試!git

這個測試用例使用了expecttoBe進行兩個值相同的測試。想要學習更多的關於jest測試,請參考 Using Matcherses6

從命令行運行

你也能夠直接從命令行執行,能夠輸入一些有空的參數(npm install jest -g)。github

Here's how to run Jest on files matching my-test, using config.json as a configuration file and display a native OS notification after the run:web

jest my-test --notify --config=config.json複製代碼

若是你想學習更多的命令行執行,請參考 Jest CLI Options正則表達式

附加配置

使用 Babel:
安裝babel-jestregenerator-runtime 包:

npm install --save-dev babel-jest regenerator-runtime複製代碼

注意: 若是你使用npm 3或者npm 4,你不用指明安裝regenerator-runtime

添加一份.babelrc文件到你的工程根目錄,好比,若是你使用es6或者react.js須要使用babel-preset-es2015babel-preset-react預設:

{
  "presets": ["es2015", "react"]
}複製代碼

這樣你會使用es6與react全部指定的語法。

注意: 若是你使用更多的babel編譯配置,請使用babel's env option,記住jest將會自動定義node_env做爲測試。

使用webpack

jest能夠實用在工程內使用webpack管理你的資產,樣式和編輯。webpack 提供一些特別的功能相比於其餘工具。更多資料請參考 webpack guide

2. 使用匹配

普通匹配

jest使用matchers讓你經過不一樣的方法測試值。你須要熟記不少不一樣的matchers。這裏只介紹最經常使用的matchers

最簡單的測試值相等的是精確相等:

test('two plus two is four', () => {
  expect(2 + 2).toBe(4);
});複製代碼

上段代碼,expect(2 + 2)返回一個「期待」對象。除了調用matchers,關於期待對象你不須要作太多。在這段代碼中,.toBe(4)是一個matcher。當jest運行時,將追蹤全部失敗的matchers,因此它能夠精確的打印錯誤信息。

toBe使用===精確等於測試。若是你想深度測試對象相等,使用toEqual代替。

test('object assignment', () => {
  const data = {one: 1};
  data['two'] = 2;
  expect(data).toEqual({one: 1, two: 2});
});複製代碼

toEqual 遞歸的查找每一個字段對比是否相等。

你可使用not去測試matcher的反面:

test('adding positive numbers is not zero', () => {
  for (let a = 1; a < 10; a++) {
    for (let b = 1; b < 10; b++) {
      expect(a + b).not.toBe(0);
    }
  }
});複製代碼

類型判斷

有時候你須要判斷undefined,null與false,但有時候你不須要明確的區分他們。jest包含工具明確的區分他們。

  • toBeNull matches only null
  • toBeUndefined matches only undefined
  • toBeDefined is the opposite of toBeUndefined
  • toBeTruthy matches anything that an if statement treats as true
  • toBeFalsy matches anything that an if statement treats as false

舉個例子:

test('null', () => {
  const n = null;
  expect(n).toBeNull();
  expect(n).toBeDefined();
  expect(n).not.toBeUndefined();
  expect(n).not.toBeTruthy();
  expect(n).toBeFalsy();
});

test('zero', () => {
  const z = 0;
  expect(z).not.toBeNull();
  expect(z).toBeDefined();
  expect(z).not.toBeUndefined();
  expect(z).not.toBeTruthy();
  expect(z).toBeFalsy();
});複製代碼

你可使用這些matcher作精確的匹配。

數字

多種途徑比較數字

test('two plus two', () => {
  const value = 2 + 2;
  expect(value).toBeGreaterThan(3);
  expect(value).toBeGreaterThanOrEqual(3.5);
  expect(value).toBeLessThan(5);
  expect(value).toBeLessThanOrEqual(4.5);

  // toBe and toEqual are equivalent for numbers
  expect(value).toBe(4);
  expect(value).toEqual(4);
});複製代碼

你可使用toBeCloseTo進行浮點比較

test('adding floating point numbers', () => {
  const value = 0.1 + 0.2;
  expect(value).not.toBe(0.3);    // 浮點數不會直接相等
  expect(value).toBeCloseTo(0.3); // 使用closeTo方法進行浮點數字比較
});複製代碼

字符串

你可使用toMatch測試正則表達式來驗證string字符串

test('there is no I in team', () => {
  expect('team').not.toMatch(/I/);
});

test('but there is a "stop" in Christoph', () => {
  expect('Christoph').toMatch(/stop/);
});複製代碼

數組

你能夠檢驗數組是否包含某一個特別項

const shoppingList = [
  'diapers',
  'kleenex',
  'trash bags',
  'paper towels',
  'beer',
];

test('the shopping list has beer on it', () => {
  expect(shoppingList).toContain('beer');
});複製代碼

表達式

你可使用toThrow來檢驗函數是否拋出異常

function compileAndroidCode() {
  throw new ConfigError('you are using the wrong JDK');
}

test('compiling android goes as expected', () => {
  expect(compileAndroidCode).toThrow();
  expect(compileAndroidCode).toThrow(ConfigError);

  // You can also use the exact error message or a regexp
  expect(compileAndroidCode).toThrow('you are using the wrong JDK');
  expect(compileAndroidCode).toThrow(/JDK/);
});複製代碼

更多

這是一個just嘗試,查看完整的matchers列表renerence docs

一旦你掌握了一個可用的matcher,建議下一步學習jest如何檢驗異步代碼

3.測試異步代碼

js運行異步代碼是很廣泛的。jest須要知道何時代碼測試已完成,才能移動到下一個測試。jest有幾種方法處理。

Callbacks

最廣泛的異步是經過回調函數。

好比,若是你調用fetchData(callback)函數去拉取異步數據而且結束時候調用callback(data)。你要測試的數據是否等於peanut buter

默認狀況下,Jest走完測試代碼就完成測試。這意味着測試不能定期進行。

// Don't do this!
test('the data is peanut butter', () => {
  function callback(data) {
    expect(data).toBe('peanut butter');
  }

  fetchData(callback);
});複製代碼

問題在於測試工做將在fetchData結束的時候,也就是在回調函數以前結束。

爲了解決這個問題,這是另外一種形式。使用參數done來代替無參函數。Jest將會延遲測試直到done回調函數執行完畢。

test('the data is peanut butter', done => {
  function callback(data) {
    expect(data).toBe('peanut butter');
    done();
  }

  fetchData(callback);
});複製代碼

若是done一直沒有調用,測試將會失敗。

Promises

若是你使用promise,有一種簡單的方法處理異步測試。你的測試代碼中Jest返回一個Promise,而且等待Promiseresolve。若是Promiserejected,測試自動失敗。

好比,仍是那個fetchData,此次使用了回調,返回一個promise假定reslove一個字符串peanut butter。測試代碼以下:

test('the data is peanut butter', () => {
  return fetchData().then(data => {
    expect(data).toBe('peanut butter');
  });
});複製代碼

確保返回一個Promise-若是你省略了return,你的測試代碼將會在fetchData以前結束。

你也可使用resolves關鍵字在你的expect代碼後,而後Jest將會把等待狀態轉換成resolve。若是promiserejected,測試自動失敗。

test('the data is peanut butter', () => {
  return expect(fetchData()).resolves.toBe('peanut butter');
});複製代碼

Async/Await

若是你使用async/await,你能夠完美嵌入測試。寫一個async測試,僅使用async關鍵字在你的matchers函數前面就能經過測試。好比,仍是fetchData這個測試方案能夠寫成

test('the data is peanut butter', async () => {
  await expect(fetchData()).resolves.toBe('peanut butter');
});複製代碼

在這種狀況下,async/await僅僅是個有效的promises樣例的邏輯語法糖。

這些表單特別優秀,你能夠混合使用這些在你的代碼庫或者單個文件中,它僅僅使你的測試變得更加簡單。

安裝與卸載

寫測試的過程當中,在你運行測試以前,你須要作一些初始化工做,去作一些須要測試的事情以前,而且你須要作一些結束工做,去作一些測試結束的事情。Jest提供了helper函數去處理這些工做。

多測試重複初始化

若是你有不少測試有重複的工做,你能夠是使用beforeEachafterEach

好比你有不少測試運行以前須要調用initializeCityDatabase(),並且測試結束 後須要調用clearCityDatabase()。你能夠這麼作:

beforeEach(() => {
  initializeCityDatabase();
});

afterEach(() => {
  clearCityDatabase();
});

test('city database has Vienna', () => {
  expect(isCity('Vienna')).toBeTruthy();
});

test('city database has San Juan', () => {
  expect(isCity('San Juan')).toBeTruthy();
});複製代碼

一樣的,若是你的initializeCityDatabase函數返回一個promise,你可使用return返回這個函數。

beforeEach(() => {
  return initializeCityDatabase();
});複製代碼

一次性設置

若是你有不少測試有共同的重複的工做,而且重複的工做只在測試開始與測試結束的地方運行一次,你能夠是使用beforeAllbeforeAll

好比你有一個測試開始以前要調用initializeCityDatabase(),並且測試結束 後須要調用clearCityDatabase()。你能夠這麼作:

beforeAll(() => {
  return initializeCityDatabase();
});

afterAll(() => {
  return clearCityDatabase();
});

test('city database has Vienna', () => {
  expect(isCity('Vienna')).toBeTruthy();
});

test('city database has San Juan', () => {
  expect(isCity('San Juan')).toBeTruthy();
});複製代碼

範圍

默認狀況下,beforeafter會對文件的每一個測試生效。若是你只想對某些測試生效,你可使用describe塊。 beforeafter僅僅會在聲明塊中運行。

好比,咱們不只須要城市初始化,還須要食物初始化,咱們能夠對不一樣的測試作不一樣的初始化。

// Applies to all tests in this file
beforeEach(() => {
  return initializeCityDatabase();
});

test('city database has Vienna', () => {
  expect(isCity('Vienna')).toBeTruthy();
});

test('city database has San Juan', () => {
  expect(isCity('San Juan')).toBeTruthy();
});

describe('matching cities to foods', () => {
  // Applies only to tests in this describe block
  beforeEach(() => {
    return initializeFoodDatabase();
  });

  test('Vienna <3 sausage',="" ()=""> {
    expect(isValidCityFoodPair('Vienna', 'Wiener Schnitzel')).toBe(true);
  });

  test('San Juan <3 plantains',="" ()=""> {
    expect(isValidCityFoodPair('San Juan', 'Mofongo')).toBe(true);
  });
});
  
  
  

 
  
  
  

 複製代碼

建議

若是你僅僅想測試一個測試用例,或者跨過某個測試用例,你可使用fitxit

fit('this will be the only test that runs', () => {
  expect(true).toBe(false);
});

xit('this will be the only test that runs', () => {
  expect(true).toBe(false);
});

test('this test will not run', () => {
  expect('A').toBe('A');
});複製代碼
相關文章
相關標籤/搜索