阅读这篇文章的人,咱们假设他们只知道在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来模拟和修改不是测试流程部分。