在2018年里关于测试JavaScript的回顾
栏目: JavaScript · 发布时间: 7年前
内容简介:这篇文章的目的是想要让你对于在2018中测试Javascript最重要的原因、时期和工具还有方法保持了解。这篇文章的内容参考了许多文章(链接列在要文底下),还包含了我们自己多年在Welldone Software Solutions里不同产品中的实现的不同测试方案的经验。阅读这篇文章的人,我们假设他们只知道在2018年的前端开发社区是怎么样测试Javascript的。这是他们分享给他们的同事、家人还有朋友的很大原因。看一下Facebook的测试框架
这篇文章的目的是想要让你对于在2018中测试Javascript最重要的原因、时期和 工具 还有方法保持了解。这篇文章的内容参考了许多文章(链接列在要文底下),还包含了我们自己多年在Welldone Software Solutions里不同产品中的实现的不同测试方案的经验。
阅读这篇文章的人,我们假设他们只知道在2018年的前端开发社区是怎么样测试Javascript的。这是他们分享给他们的同事、家人还有朋友的很大原因。
- 我对这篇文章做了大量的调查,如果你发现任何错误,请在下面评论,我会第一时间纠正它。
- 留意文章底下的链接,阅读它们会让你对整个大局有所了解并使你成功这方面的专家(理论上)。
- 最好的实践这篇文章的方法是选择一个你所需要的测试类型,再选择若干看起来适合你的工具,然后再测试他们。我们正处于一个有大量工具和实践的环境。你的目标就是筛选它们然后对你的独立案例作最好的组合。
介绍
看一下Facebook的测试框架 Jest 的Logo:
正如你所看到的,它的口号是保证它是一个“不痛苦的”JavaScript测试框架,不过正如有些人在评论区中指出:
(没有什么测试是不痛苦的)
的确,Facebook使用这个口号有一个很重要的原因。通常JavaScript开发者对网站测试是非常痛苦的,JS测试总是受限制、难以实现,速度慢和有时非常昂贵。
不管怎么样,在对的策略和在对的工具组合下,一个几乎全覆盖的测试是可以实现的,而且成这个测试是非常容易管理、简单而且相对较快。
测试类型
你可以在这里、 这里 还有这里更深入的了解不同的测试类型。通常情况下,对于网站来说最重要的测试类型是:
- 单元测试:提供一些输入数据来测试每个独立的函数或者类,并且保证他们的输出是符合预期的。
- 集成测试:测试流程或者组件的表现是符合预期的,包含副作用。
- UI测试:(A.K.A 功能测试)在不管内部结构实现的情况下,通过控制浏览器或者网站,测试产品本身的流程是否可以达到预期表现。
测试工具类型
测试工具可以分为以下几类。有一些工具仅仅只提供一个功能,有一些则提供了一个包含许多功能的工具包。
为了后期可以实现更复杂的功能,我们常常会使用工具包,哪怕我们一开始只是使用他其中的一个功能。
- 提供测试结构(Testing structure)(Mocha, Jasmine , Jest , Cucumber )
- 提供断言函数(Assertion functions)(Chai, Jasmine , Jest , Unexpected )
- 创建、显示和监听测试结果(Mocha, Jasmine , Jest , Karma )
- 创建和对比组件和数据结构的快照,从而保证对比上一次运行的改变是符合预期的(Jest, Ava )
- 提供mocks, spies, and stubs (Sinon, Jasmine , enzyme , Jest , testdouble )
- 创建代码覆盖率报告(Istanbul, Jest , Blanket )
- 提供可以执行用户行为的浏览器环境或类浏览器环境 (Protractor, Nightwatch , Phantom , Casper )
让我们来解释一下上面提到的一些东西:
测试结构(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 和应用中不同单元的内部结构。
浏览器或类浏览器环境可以是它三个中的其中一个:
- jsdom ——你的纯JavaScript的环境来模拟真实的浏览器。他没有界面并不渲染任何东西。它提供window、document,body,location,cookies,selectors和你在浏览器中运行JS时你想用到的任何东西。
- 无头浏览器环境——一个浏览器在没有界面的情况下运行,目的是为了让浏览器的响应更快。
- 真实的浏览器环境——打开一个真实的浏览器并运行你的测试
把所有东西组合在一起
我们建议尽可能地用同一套工具来执行所有的测试类型:相同的测试结构和语法,断言函数,测试报告,监听机制。
我们同样建议使用两个不同的测试流程。一个是单元和集成测试,另一个是UI测试。因为UI测试会耗费大量的时间,特别是测试不同的浏览器环境和不同的设备环境上的浏览器,它会相当耗费精力,所以你应该尽可能地少在首要的流程中运行它。几个例子:只有在合并新功能分支的情况下运行。
单元测试
应该覆盖应用中所有小的单元——utils,services和helpers。为所有这些单元提供简单的边缘情况下的输入,并使用断言函数来确保单元的输出是期望的。当然也要使用覆盖率报告工具来知道哪些单元是被测试覆盖的。
单元测试要尽可能的使用函数工编程和纯函数的原因之一是,你的应用越纯,你的测试就越简单。
以上所述就是小编给大家介绍的《在2018年里关于测试JavaScript的回顾》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
测试驱动开发的艺术
Lasse Koskela / 李贝 / 人民邮电出版社 / 20101023 / 59.00元
在传统的软件开发中,开发人员对于代码是否正确心中无底,一切依赖于后期的测试环节。极限编程反其道而行之,主张采用测试驱动开发(TDD)的方法,即通过测试定义所要开发的功能的接口,然后实现功能的开发过程。TDD通过不断地测试推动代码的开发,既简化了代码,又保证了软件质量。 本书采用“手把手”的教学方式,通过大量实例来解释TDD,还专门用几章的篇幅来讲解如何为难于测试的技术编写单元测试。全书内容循......一起来看看 《测试驱动开发的艺术》 这本书的介绍吧!