閱讀這篇文章的人,咱們假設他們只知道在2018年的前端開發社區是怎麼樣測試Javascript的。這是他們分享給他們的同事、家人還有朋友的很大緣由。javascript
看一下Facebook的測試框架Jest
的Logo:前端
正如你所看到的,它的口號是保證它是一個「不痛苦的」JavaScript測試框架,不過正若有些人在評論區中指出:java
(沒有什麼測試是不痛苦的)git
的確,Facebook使用這個口號有一個很重要的緣由。一般JavaScript開發者對網站測試是很是痛苦的,JS測試老是受限制、難以實現,速度慢和有時很是昂貴。github
無論怎麼樣,在對的策略和在對的工具組合下,一個幾乎全覆蓋的測試是能夠實現的,並且成這個測試是很是容易管理、簡單並且相對較快。編程
你能夠在這裏、這裏還有這裏更深刻的瞭解不一樣的測試類型。一般狀況下,對於網站來講最重要的測試類型是:json
測試工具能夠分爲如下幾類。有一些工具僅僅只提供一個功能,有一些則提供了一個包含許多功能的工具包。redux
爲了後期能夠實現更復雜的功能,咱們經常會使用工具包,哪怕咱們一開始只是使用他其中的一個功能。api
讓咱們來解釋一下上面提到的一些東西:promise
測試結構(Testing structure)是指你的測試行爲。一般測試都是運行在行爲驅動開發的BDD的模式下的。他一般看起來像這樣子:
describe('calculator', function() {
// describes a module with nested "describe" functions
describe('add', function() {
// specify the expected behavior
it('should add 2 numbers', function() {
//Use assertion functions to test the expected behavior
...
})
})
})
複製代碼
斷言函數(Assertion functions)是指保證測試變量通過斷言函數後會返回指望值。他一般看起來像這樣,最多見的是第一次個和第二個例子:
// Chai expect (popular)
expect(foo).to.be.a('string')
expect(foo).to.equal('bar')
// Jasmine expect (popular)
expect(foo).toBeString()
expect(foo).toEqual('bar')
// Chai assert
assert.typeOf(foo, 'string')
assert.equal(foo, 'bar')
// Unexpected expect
expect(foo, 'to be a', 'string')
expect(foo, 'to be', 'bar')
複製代碼
提示:這裏有一篇很棒的文章講解了關於
Jasmine
的高級斷言。
Spies爲咱們提供了關於函數的信息——好比說,這個函數被調用了多少次,在什麼狀況下調用,被誰調用等等。
他們一般用在集成測試裏,保證含有反作用的流程能夠被正常測試出指望結果。這個例子,這個計算函數在某些流程裏被調用了多少次?
it('should call method once with the argument 3', () => {
// create a sinon spy to spy on object.method
const spy = sinon.spy(object, 'method')
// call the method with the argument "3"
object.method(3)
// make sure the object.method was called once, with the right arguments
assert(spy.withArgs(3).calledOnce)
})
複製代碼
Stubbing or dubbing把某些函數替換成爲特定的函數,在這個特定函數的做用下保證行爲是正常表現的。
// Sinon
sinon.stub(user, 'isValid').returns(true)
// Jasmine stubs are actually spies with stubbing functionallity
spyOn(user, 'isValid').andReturns(true)
複製代碼
在promises
的狀況下會像這樣:
it('resolves with the right name', done => {
// make sure User.fetch "responds" with our own value "David"
const stub = sinon
.stub(User.prototype, 'fetch')
.resolves({ name: 'David' })
User.fetch()
.then(user => {
expect(user.name).toBe('David')
done()
})
})
複製代碼
Mocks or Fakes 模擬一些特定模塊或行爲來測試不一樣狀況下的流程。
據個例子,在測試中,Sinon能夠經過模擬一個服務接口來保證在離線的狀況下能夠獲得快速的指望響應。
it('returns an object containing all users', done => {
// create and configure the fake server to replace the native network call
const server = sinon.createFakeServer()
server.respondWith('GET', '/users', [
200,
{ 'Content-Type': 'application/json' },
'[{ "id": 1, "name": "Gwen" }, { "id": 2, "name": "John" }]'
])
// call a process that includes the network request that we mocked
Users.all()
.done(collection => {
const expectedCollection = [
{ id: 1, name: 'Gwen' },
{ id: 2, name: 'John' }
]
expect(collection.toJSON()).to.eql(expectedCollection)
done()
})
// respond to the request
server.respond()
// remove the fake server
server.restore()
})
複製代碼
快照測試是指你拿的一個數據和另外一個指望的數據進行對比。
下面的例子來源於Jest的官方文檔,他展現了的一個link
組件的快照測試。
it('renders correctly', () => {
// create an instance of the Link component with page and child text
const linkInstance = (
<Link page="http://www.facebook.com">Facebook</Link>
)
// create a data snapshot of the component
const tree = renderer.create(linkInstance).toJSON()
// compare the sata to the last snapshot
expect(tree).toMatchSnapshot()
})
複製代碼
他不會爲這個組件進行渲染而且保存成一張圖片,可是它能夠把它的內部結構保存在一個單獨的文件中,像這樣子:
exports[`renders correctly 1`] = `
<a
className="normal"
href="http://www.facebook.com"
onMouseEnter={[Function]}
onMouseLeave={[Function]}
>
Facebook
</a>
`;
複製代碼
當測試運行時,而且有一個不一樣於上一次的快照,開發者能夠及時的知道它們之間不一樣的地方。
注意:快照一般是用來對比組件的結構數據,可是他們也能夠用來對比其餘類型的數據,像redux stores 和應用中不一樣單元的內部結構。
瀏覽器或類瀏覽器環境能夠是它三個中的其中一個:
咱們建議儘量地用同一套工具來執行全部的測試類型:相同的測試結構和語法,斷言函數,測試報告,監聽機制。
咱們一樣建議使用兩個不一樣的測試流程。一個是單元和集成測試,另外一個是UI測試。由於UI測試會耗費大量的時間,特別是測試不一樣的瀏覽器環境和不一樣的設備環境上的瀏覽器,它會至關耗費精力,因此你應該儘量地少在首要的流程中運行它。幾個例子:只有在合併新功能分支的狀況下運行。
應該覆蓋應用中全部小的單元——utils,services和helpers。爲全部這些單元提供簡單的邊緣狀況下的輸入,並使用斷言函數來確保單元的輸出是指望的。固然也要使用覆蓋率報告工具來知道哪些單元是被測試覆蓋的。
單元測試要儘量的使用函數工編程和純函數的緣由之一是,你的應用越純,你的測試就越簡單。
這種測試專一在單元測試和應用的結果,它是測試在應用許多單元是正常的,可是全部單元整全起來的流程倒是失敗的狀況。
集成測試(包括快照),在另外一方面,能夠檢測出許多由於你修改一個東西或者刪除一個東西所形成的意外錯誤。
它也使咱們記得在現實生活中,許多緣由包括不完美的產品設計,大範圍使用黑箱,不是全部單元都是純的,不是全部單元都是能夠測試等。一些單元只須要測試大流程的一部分。
集成測試能夠覆蓋重要的跨流程模塊。相對於單元測試,你可使用spies來替換反作用,從而保證輸出能夠被斷言。你可使用stubs來模擬和修改不是測試流程部分。