干休息 API 测试
DRY Rest API testing
我正在用 mocha 和 chai 对休息 api 进行单元测试。目前,对于每个请求,(例如 POST)我测试整个响应主体(不包括非静态数据,如 ids 等...)。问题是:如果有一天我决定更改我的资源模型,比如添加一个新字段。实际测试不会检查这个新字段。所以我必须更新与此资源相关的每个测试,随着测试数量的增加,这可能会变得复杂。
所以我的问题是:我的做法是否正确?如果不是,我应该在我的 api 回复中测试什么,我不应该测试什么?
DRY (Don't repeat yourself) principle applies to testing as well, but don't overdo it. Tests should be "DAMP not DRY".
...if some day I decide to change the model of my resource, like maybe adding a new field. The actual tests won't check this new field. So I'll have to update every test related to this resource...
在这种情况下,我通常做的是创建一个 Chai custom assertion 来为特定类型定义一组断言,例如 Todo
。
然后(重新)在所有相关测试中使用此自定义断言,以验证返回的对象是否确实通过了 Todo
的断言,而不必在每个测试中重复相同的断言.
这是一个例子:
const chai = require('chai')
const chaiHttp = require('chai-http')
const server = 'https://jsonplaceholder.typicode.com'
chai.should()
chai.use(chaiHttp)
// Define a custom assertion for 'Todo'. We expect that a Todo always
// has an `id` property of type `Number` and a `title` property of
// type `String`.
chai.use((chai, utils) => {
utils.addProperty(chai.Assertion.prototype, 'Todo', function () {
this._obj.should.have.property('id')
this._obj.should.have.property('title')
this._obj.id.should.be.a('Number')
this._obj.title.should.be.a('String')
})
})
// Begin Tests
describe('Retrieve a Todo', () => {
it('returns a single Todo by ID', () => {
return chai.request(server)
.get('/todos/1')
.then(res => {
res.should.have.status(200)
// Use the custom assertion to check if returned object
// passes the assertions for `Todo`.
res.body.should.be.a.Todo
})
})
})
describe('Retrieve all Todos', () => {
it('returns a list containing 200 Todos', () => {
return chai.request(server)
.get('/todos')
.then(res => {
res.should.have.status(200)
res.body.should.be.an('Array')
res.body.should.have.length(200)
// Reuse the custom assertion to check if all returned objects
// pass the assertions for `Todo`.
res.body.forEach(todo => todo.should.be.a.Todo)
})
})
})
如果将来我在 Todo
上添加一个新字段,即 completed
,我需要做的就是像这样修改自定义断言:
chai.use((chai, utils) => {
utils.addProperty(chai.Assertion.prototype, 'Todo', function () {
this._obj.should.have.property('id')
this._obj.should.have.property('title')
this._obj.should.have.property('completed')
this._obj.id.should.be.a('Number')
this._obj.title.should.be.a('String')
this._obj.completed.should.be.a('Boolean')
})
})
... what should I test in my api responses and what should I not ?
至少我会检查是否:
- 响应 HTTP status 正确。
- 每个 属性 的响应主体都有适当的属性和正确的类型。
- 如果响应是一个项目列表,我会检查响应正文是否确实是一个
Array
,如果它有 length
我希望它有,如果Array
具有正确的属性和类型。
这里没有"rules"。最后是 risk/time 的决定。维护测试套件需要时间。如果我正在构建一个简单的待办事项应用程序供自己使用,我不会太担心详尽的测试。但是,如果我正在构建一个 public 支付服务器,我肯定希望我的测试尽可能详尽。
我正在用 mocha 和 chai 对休息 api 进行单元测试。目前,对于每个请求,(例如 POST)我测试整个响应主体(不包括非静态数据,如 ids 等...)。问题是:如果有一天我决定更改我的资源模型,比如添加一个新字段。实际测试不会检查这个新字段。所以我必须更新与此资源相关的每个测试,随着测试数量的增加,这可能会变得复杂。
所以我的问题是:我的做法是否正确?如果不是,我应该在我的 api 回复中测试什么,我不应该测试什么?
DRY (Don't repeat yourself) principle applies to testing as well, but don't overdo it. Tests should be "DAMP not DRY".
...if some day I decide to change the model of my resource, like maybe adding a new field. The actual tests won't check this new field. So I'll have to update every test related to this resource...
在这种情况下,我通常做的是创建一个 Chai custom assertion 来为特定类型定义一组断言,例如 Todo
。
然后(重新)在所有相关测试中使用此自定义断言,以验证返回的对象是否确实通过了 Todo
的断言,而不必在每个测试中重复相同的断言.
这是一个例子:
const chai = require('chai')
const chaiHttp = require('chai-http')
const server = 'https://jsonplaceholder.typicode.com'
chai.should()
chai.use(chaiHttp)
// Define a custom assertion for 'Todo'. We expect that a Todo always
// has an `id` property of type `Number` and a `title` property of
// type `String`.
chai.use((chai, utils) => {
utils.addProperty(chai.Assertion.prototype, 'Todo', function () {
this._obj.should.have.property('id')
this._obj.should.have.property('title')
this._obj.id.should.be.a('Number')
this._obj.title.should.be.a('String')
})
})
// Begin Tests
describe('Retrieve a Todo', () => {
it('returns a single Todo by ID', () => {
return chai.request(server)
.get('/todos/1')
.then(res => {
res.should.have.status(200)
// Use the custom assertion to check if returned object
// passes the assertions for `Todo`.
res.body.should.be.a.Todo
})
})
})
describe('Retrieve all Todos', () => {
it('returns a list containing 200 Todos', () => {
return chai.request(server)
.get('/todos')
.then(res => {
res.should.have.status(200)
res.body.should.be.an('Array')
res.body.should.have.length(200)
// Reuse the custom assertion to check if all returned objects
// pass the assertions for `Todo`.
res.body.forEach(todo => todo.should.be.a.Todo)
})
})
})
如果将来我在 Todo
上添加一个新字段,即 completed
,我需要做的就是像这样修改自定义断言:
chai.use((chai, utils) => {
utils.addProperty(chai.Assertion.prototype, 'Todo', function () {
this._obj.should.have.property('id')
this._obj.should.have.property('title')
this._obj.should.have.property('completed')
this._obj.id.should.be.a('Number')
this._obj.title.should.be.a('String')
this._obj.completed.should.be.a('Boolean')
})
})
... what should I test in my api responses and what should I not ?
至少我会检查是否:
- 响应 HTTP status 正确。
- 每个 属性 的响应主体都有适当的属性和正确的类型。
- 如果响应是一个项目列表,我会检查响应正文是否确实是一个
Array
,如果它有length
我希望它有,如果Array
具有正确的属性和类型。
这里没有"rules"。最后是 risk/time 的决定。维护测试套件需要时间。如果我正在构建一个简单的待办事项应用程序供自己使用,我不会太担心详尽的测试。但是,如果我正在构建一个 public 支付服务器,我肯定希望我的测试尽可能详尽。