在上一部分中,我着眼于我正在开发的团队实现可以用此测试描述的行为的事实:
it('execute should return promise', () => { request.configure(options); request.execute().then((result) => { expect(result.Id).toEqual(1); expect(result.Name).toEqual('Jack'); }); });
在我看来,得到Promise
并对其进行处理并不是我想要的。 如果团队本身执行此例行工作会更好,然后将结果放置在例如Redux
存储库中。 我将尝试重写现有测试,以便在其中表达我的新期望:
const store = require('../../src/store'); const DbMock = require('../mocks/DbMock'); const db = new DbMock(); const Request = require('../../src/storage/Request'); const options = { tableName: 'users', query: { Id: 1 } }; let request = null; beforeEach(() => { request = new Request(db, store); }); it('should dispatch action if record exists', () => { request.configure(options); request.execute(() => { const user = store.getState().user; expect(user.Id).toEqual(options.query.Id); expect(user.Name).toEqual('Jack'); }); });
尽管实际上我现在必须教Request
类的execute
方法来执行回调方法(如果用户将其作为参数传递),但这可能更方便。 您不能没有它,因为我想在内部execute
中使用异步调用,只有在确信执行完成后才能测试其测试结果。
进一步...看代码的第一行,我了解到可以返回编辑Request
类的代码之前,我需要将Redux
包添加到项目中,为其实现至少一个
然后将此Stored单独打包。 第一个测试可能是变速箱:
const reduce = require('../../src/reducers/user'); it('should return new state', () => { const user = { Id: 1, Name: 'Jack'}; const state = reduce(null, {type: 'USER', user }); expect(state).toEqual(user); });
我运行测试并同意Jasmine
观点,除了所有先前的错误外,没有找到名为../../src/reducers/user
的模块。 因此,我将编写它,尤其是因为它承诺很小并且非常可预测:
const user = (state = null, action) => { switch (action.type) { case 'USER': return action.user; default: return state; } }; module.exports = user;
我进行测试,但看不到根本的改进。 这是因为模块../../src/store
(我在我的Request
类的测试中假设存在)尚未实现。 而且还没有测试给他。 我将从测试开始:
describe('store', () => { const store = require('../src/store'); it('should reduce USER', () => { const user = { Id: 1, Name: 'Jack' }; store.dispatch({type: 'USER', user }); const state = store.getState(); expect(state.user).toEqual(user); }); });
测试? 关于缺少store
模块的报道更多,因此我将立即对其进行处理。
const createStore = require('redux').createStore; const combineReducers = require('redux').combineReducers; const user = require('./reducers/user'); const reducers = combineReducers({user}); const store = createStore(reducers); module.exports = store;
了解到我将拥有多个变速箱,因此在实现
领先一些,并在组装时使用combineReducers
方法。 我再次运行测试,并看到一条新的错误消息,该消息告诉我Request
类的execute
方法不能按我的测试建议工作。 作为execute方法的结果,用户记录未出现在
。 是时候重构Request
类了。
我记得现在对execute
方法的测试是什么样的:
it('should dispatch action if record exists', () => { request.configure(options); request.execute(() => { const user = store.getState().user; expect(user.Id).toEqual(options.query.Id); expect(user.Name).toEqual('Jack'); }); });
然后,我将修复方法本身的代码,以便测试可以执行:
execute(callback){ const table = this.db.Model.extend({ tableName: this.options.tableName }); table.where(this.options.query).fetch().then((item) => { this.store.dispatch({ type: 'USER', user: item }); if(typeof callback === 'function') callback(); }); }
我将输入npm test
控制台,然后... Bingo! 我的请求不仅学会了从数据库接收数据,还学会了将其保存在将来处理过程的
中,以便后续操作可以毫无问题地接收此数据。
但是! 我的处理程序只能将一种类型的操作分派到
,这极大地限制了其功能。 但是我想在需要从数据库中提取一些记录并将其分派到
单元中以便在需要的键下进行进一步处理时重复使用此代码。 因此,我再次开始重构测试:
const options = { tableName: 'users', query: { Id : 1 }, success (result, store) { const type = 'USER'; const action = { type , user: result }; store.dispatch(action); } }; it('should dispatch action if record exists', () => { request.configure(options); request.execute(() => { const user = store.getState().user; expect(user.Id).toEqual(options.query.Id); expect(user.Name).toEqual('Jack'); }); });
在我看来,从异常功能中保存Request
类以处理查询结果将是很好的。 语义上的Request
是一个请求。 完成请求,收到答案,任务完成,尊重班级唯一责任的原则。 然后让经过此过程训练有素的人来处理结果,唯一的责任应该是处理本身的某个版本。 因此,我决定将success
方法传递给查询设置,该方法负责处理请求成功返回的数据。
测试,现在您无法运行。 我从理智上理解。 我没有在测试本身中进行任何修复,也没有在实现中进行任何更改,因此测试应继续成功运行。 但是从情感npm test
,我需要运行npm test
命令并执行它,然后继续在Request
类中编辑我的execute
方法的实现,以store.dispatch(...)
的调用替换为对this.options.success(...)
:
execute(callback){ const table = this.db.Model.extend({ tableName: this.options.tableName }); table.where(this.options.query).fetch().then((item) => { this.options.success(item, this.store); if(typeof callback !== 'undefined') callback(); }); }
我进行测试。 瞧! 测试是完全绿色的。 生活越来越好! 接下来是什么? 我立即看到您需要更改测试的标题,因为它与实际情况不太一致。 该测试不会检查是否由于请求而发生了方法的分派,而是检查了查询是否更新了容器中的状态。 因此,我将测试标题更改为...,例如:
it('should update store user state if record exists', () => { request.configure(options); request.execute(() => { const user = store.getState().user; expect(user.Id).toEqual(options.query.Id); expect(user.Name).toEqual('Jack'); }); });
接下来是什么? 然后我认为是时候应该注意了,不是我的请求会返回错误而不是请求的数据的情况。 这不是不可能的情况。 对不对 最主要的是,在这种情况下,我将无法编写所需的数据集并将其发送给我的KYC运算符,以便与我编写所有这些代码进行集成。 是这样吗 所以 首先,我将编写一个测试:
it('should add item to store error state', () => { options.query = { Id: 555 }; options.error = (error, store) => { const type = 'ERROR'; const action = { type, error }; store.dispatch(action); }; request.configure(options); request.execute(() => { const error = store.getState().error; expect(Array.isArray(error)).toBeTruthy(); expect(error.length).toEqual(1); expect(error[0].message).toEqual('Something goes wrong!'); }); });
我不知道测试的结构是否表明我决定节省时间和金钱,并编写最少的代码来检查请求是否返回错误? 没看到吗?
我不想浪费时间对TableMock
其他实现进行TableMock
,这会模仿错误。 我决定,目前对现有实现中的几个条件构造对我来说已经足够了,并建议可以通过查询query
参数进行调整。 所以我的假设是:
- 如果
options.query
查询中的Id
是1 ,那么我的伪表将始终返回允许的Promise
以及集合中的第一条记录。 - 如果
options.query
查询中的Id
为555 ,那么我的伪表将始终返回一个内部包含Error
实例的拒绝的Promise
,其message
内容为Something出问题了! 。
当然,这远非理想。 实施相应的DbMock
实例(例如, HealthyDbMock
, FaultyDbMock
, EmptyDbMock
对于感知来说将更加可读和方便。 从名称中可以立即看出,第一个将始终正确运行,第二个将始终错误运行,至于第三个,我们可以假定它将始终返回null
而不是结果。 也许,在以上述方式检查了我的第一个假设之后,在我看来似乎花费了最少的时间,我将DbMock
实现另外两个模拟不健康行为的DbMock
实例。
我进行测试。 我收到
缺少所需属性的预期错误,并且正在写另一个测试。 这次是变速箱,它将处理ERROR
类型的动作。
describe('error', () => { const reduce = require('../../src/reducers/error'); it('should add error to state array', () => { const type = 'ERROR'; const error = new Error('Oooops!'); const state = reduce(undefined, { type, error }); expect(Array.isArray(state)).toBeTruthy(); expect(state.length).toEqual(1); expect(state.includes(error)).toBeTruthy(); }); });
再次运行测试。 一切正常,已将其他错误添加到现有错误中。 我意识到
:
const error = (state = [], action) => { switch (action.type) { case 'ERROR': return state.concat([action.error]); default: return state; } }; module.exports = error;
我再次运行测试。 新的reducer可以按预期工作,但是我仍然需要确保它连接到存储库并处理预期的操作。 因此,我正在为现有的存储测试套件编写其他测试:
it('should reduce error', () => { const type = 'ERROR'; const error = new Error('Oooops!'); store.dispatch({ type, error }); const state = store.getState(); expect(Array.isArray(state.error)).toBeTruthy(); expect(state.error.length).toEqual(1); expect(state.error.includes(error)).toBeTruthy(); });
我进行测试。 一切都在期待之中。 类型为ERROR
操作不会处理现有存储。 修改现有存储库初始化代码:
const createStore = require('redux').createStore; const combineReducers = require('redux').combineReducers; const user = require('./reducers/user'); const error = require('./reducers/error'); const reducers = combineReducers({ error, user }); const store = createStore(reducers); module.exports = store;
第一百次,他撒网了……非常好! 现在,存储库将收到的错误消息累积在单独的容器属性中。
现在,我将在现有的TableMock
实现中引入几个条件构造,教它以这种方式返回一些错误来启动一些查询。 更新后的代码如下所示:
class TableMock { constructor(array){ this.container = array; } where(query){ this.query = query; return this; } fetch(){ return new Promise((resolve, reject) => { if(this.query.Id === 1) return resolve(this.container[0]); if(this.query.Id === 555) return reject(new Error('Something goes wrong!')); }); } } module.exports = TableMock;
我运行测试,并在Request
类的execute
方法中收到有关未处理的Promise
拒绝的消息。 我添加缺少的代码:
execute(callback){ const table = this.db.Model.extend({ tableName: this.options.tableName }); table.where(this.options.query).fetch().then((item) => { this.options.success(item, this.store); if(typeof callback === 'function') callback(); }).catch((error) => { this.options.error(error, this.store); if(typeof callback === 'function') callback(); }); }
然后我再次运行测试。 还有??? 实际上,没有对Request
类的execute
方法进行测试,这一测试:
it('should add item to store error state', () => { options.query = { Id: 555 }; options.error = (error, store) => { const type = 'ERROR'; const action = { type, error }; store.dispatch(action); }; request.configure(options); request.execute(() => { const error = store.getState().error; expect(Array.isArray(error)).toBeTruthy(); expect(error.length).toEqual(1); expect(error[0].message).toEqual('Something goes wrong!'); }); });
他成功完成。 因此,可以考虑在错误处理方面的查询功能。 另一个测试失败了,该测试检查存储库中的错误处理。 问题是我的存储实现模块在所有测试中都向所有使用者返回相同的静态存储实例。 在这方面,由于错误的分配已经在两个测试中发生,因此在其中一个测试中,不必通过容器中的错误数量的验证。 因为到测试开始时,容器中已经存在一个错误,并且在测试启动过程中又添加了一个错误。 所以这是这段代码:
const error = store.getState().error; expect(error.length).toEqual(1);
引发异常,报告表达式error.length
实际上是2,而不是1。现在,我将通过简单地将存储初始化代码直接转移到存储测试初始化代码来解决此问题:
describe('store', () => { const createStore = require('redux').createStore; const combineReducers = require('redux').combineReducers; const user = require('../src/reducers/user'); const error = require('../src/reducers/error'); const reducers = combineReducers({ error, user }); const store = createStore(reducers); it('should reduce USER', () => { const user = { Id: 1, Name: 'Jack' }; store.dispatch({type: 'USER', user }); const state = store.getState(); expect(state.user).toEqual(user); }); it('should reduce error', () => { const type = 'ERROR'; const error = new Error('Oooops!'); store.dispatch({ type, error }); const state = store.getState(); expect(Array.isArray(state.error)).toBeTruthy(); expect(state.error.length).toEqual(1); expect(state.error.includes(error)).toBeTruthy(); }); });
现在,该测试的初始化代码看起来有些膨胀,但是稍后我可以返回其重构。
我进行测试。 瞧! 所有测试已完成,您可以休息一下。