如何使用 Mocha、Chai 和 Sinon 为 Flux Stores 编写单元测试
介绍
Flux 中的存储相当于 MVC 应用程序中的模型。虽然它们不作为单个模型或集合,但它们存储和维护应用程序内的域。您可以说存储从所述域的模型中检索数据,并使用它来向 API 呈现应用程序的状态。
在本指南中,您将学习如何创建商店。本案例研究假设您有一个博客平台,并且希望为其编写帖子列表。本指南涵盖两个功能:
- 显示您发布的帖子并可以按日期排序
- 创建新的草稿并列出旧的草稿
入门
本指南使用 Mocha、Chai 和 Sinon 分别作为测试运行器、断言库和方法监视器。按如下方式开始:
var expect = require('chai').expect;
describe('Post List Store', function() {
it ('exists', function() {
expect(PostListStore).to.exist;
});
});
以上代码是一个简单的测试。继续阅读以使您的 PostListStore 焕发活力。
var PostsListStore = {};
module.exports = PostsListStore;
显示和排序帖子
现在,尝试编写一个测试,以便商店可以向应用程序的其余部分显示已发布帖子的列表。
在开始之前,您需要了解原始数据的类型和结构,即测试代码时将使用的数据。作为本指南中的示例,这是您的数据:
var Posts = [
{
title: 'Creating Flux Store driven by tests',
author: 'cparker',
excerpt: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris at.',
published_at: 'Nov 12 2019',
},
{
title: 'The most beautiful draft ever made by ...',
author: 'cparker',
excerpt: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris at.',
published_at: null
},
{
title: 'Best free stock photos',
author: 'cparker',
excerpt: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris at.',
published_at: 'Nov 1 2019'
},
{
title: 'Spookiest Halloween costumes',
author: 'cparker',
excerpt: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris at.',
published_at: 'Oct 30 2019'
}
];
使用上述数据作为您的内存数据库。商店查询数据时,帖子数组必须可用。在实际应用中,将此数组替换为您的模型或集合,或两者皆可。
var expect = require('chai').expect;
var mockery = require('mockery');
var Posts = require('./mockedPosts.js');
var PostsListStore;
describe('Post List Store', function() {
beforeEach(function() {
mockery.enable({
warnOnReplace: true,
warnOnUnregistered: false,
useCleanCache: true,
});
mockery.registerMock('./posts.js', Posts);
PostsListStore = require('../src/PostsListStore.js');
});
it ('exists', function() {
expect(PostsListStore).to.exist;
});
describe ('get published posts', function() {
it ('returns only posts with a published date', function() {
var posts = PostsListStore.getPublishedPosts();
posts.forEach(function(post) {
expect(post.published_at).to.not.be.null;
});
});
});
afterEach(function() {
mockery.disable();
});
});
接下来,使用以下代码使测试通过:
var Posts = require('./posts.js');
var PostsListStore = {
getPublishedPosts: function() {
return Posts.filter(function(post) {
return post.published_at;
});
},
};
从根本上来说,Stores 提供了只读 API。换句话说,从理论上讲,包括依赖注入在内的所有内容现在都可以在测试对象上打开 API。这意味着您可以将变异模型重新注入到 Store 中并绕过其容量。
本指南使用 Mockery,这是一个动态库,可让您模拟所需的模块。您只需将模拟的帖子数组放在所需的“posts.js”的位置即可。
另一个选择是使用 Jest。但是,Jest 会模拟一切。更好的方法是准确了解您正在模拟的内容。模拟可能表明您已对测试主题创建了依赖关系。这些迹象有助于提高测试的信心。
现在您可以检索已发布的帖子。接下来您必须按日期对它们进行排序。
您可能会考虑检查每篇文章的发布日期是否小于之前的文章的发布日期:
it ('returned posts are sorted by date', function() {
var posts = PostsListStore.getPublishedPosts();
var previous_date = new Date();
posts.forEach(function(post) {
var published_at = new Date(post.published_at);
expect(published_at.getTime()).to.be.below(previous_date.getTime());
previous_date = published_at;
});
});
这不是一种有效的方法。请尝试使用排序调用:
var Posts = require('./posts.js');
var PostsListStore = {
getPublishedPosts: function() {
var posts = Posts.filter(function(post) {
return post.published_at;
});
posts.sort(function(a_post, another_post) {
return new Date(another_post.published_at) - new Date(a_post.published_at);
});
return posts;
}
};
module.exports = PostsListStore;
您已创建了第一个功能,并了解了如何模拟数据以测试您的商店。让我们继续下一个功能。
创建新草稿并显示完整列表
假设草稿尚未发布,其发布日期为空:
describe ('get the drafts', function() {
it ('returns posts without a published_at date', function() {
var posts = PostsListStore.getDrafts();
posts.forEach(function(post) {
expect(post.published_at).to.be.null;
});
});
});
这个测试很简单:
getDrafts: function() {
return Posts.filter(function(post) {
return post.published_at === null;
});
},
继续阅读以了解如何添加新的草稿并向订阅的组件指示商店状态的变化。
请记住,存储是只读的。这意味着您无法通过调用 addDraft ()方法添加新草稿,因为这可能会导致集合突变。另一方面,Flux 中的视图会分派一个事件:
it ('catches dispatched event and creates a new draft', function() {
Dispatcher.dispatch({
actionType: ActionTypes.NEW_DRAFT,
draft: {
title: 'Productivity tips with Git',
author: 'cparker',
excerpt: ''
}
});
var drafts = PostsListStore.getDrafts();
var new_draft_is_in_array = false;
drafts.forEach(function(draft) {
new_draft_is_in_array = draft.title == 'Productivity tips using Git';
});
expect(new_draft_is_in_array).to.be.true;
});
接下来,您必须将商店注册到调度程序,并在触发NEW_DRAFT后创建草稿:
Dispatcher.register(function(payload) {
switch(payload.actionType) {
case ActionTypes.NEW_DRAFT:
var draft = payload.draft;
draft.published_at = null;
Posts.push(draft);
break;
}
});
该模型现在已通过调度程序的注册回调进行了变异。
现在,您对数据的修改必须广播给其他订阅的组件,否则他们将不会意识到应用程序的状态已经发生变化。
it ('a callback can be subscribed to the Store changes', function() {
var callback = sinon.spy();
PostsListStore.addChangeListener(callback);
PostsListStore.emitChange();
expect(callback.calledOnce).to.be.true;
});
接下来,将EventEmitter属性引入到你的商店:
var Posts = require('./posts.js');
var Dispatcher = require('./dispatcher.js');
var ActionTypes = require('./actionTypes.js');
var assign = require('object-assign');
var EventEmitter = require('events').EventEmitter;
var CHANGE_EVENT = 'change';
var PostsListStore = assign({}, EventEmitter.prototype, {
getDrafts: function() {
return Posts.filter(function(post) {
return post.published_at === null;
});
},
getPublishedPosts: function() {
var posts = Posts.filter(function(post) {
return post.published_at;
});
posts.sort(function(a_post, another_post) {
return new Date(another_post.published_at) - new Date(a_post.published_at);
});
return posts;
},
addChangeListener: function(callback) {
this.on(CHANGE_EVENT, callback);
},
emitChange: function() {
this.emit(CHANGE_EVENT);
}
});
免责声明:本内容来源于第三方作者授权、网友推荐或互联网整理,旨在为广大用户提供学习与参考之用。所有文本和图片版权归原创网站或作者本人所有,其观点并不代表本站立场。如有任何版权侵犯或转载不当之情况,请与我们取得联系,我们将尽快进行相关处理与修改。感谢您的理解与支持!
请先 登录后发表评论 ~