Stroybook的基本使用

原文:Stroybook的基本使用

Storybook 作为一个 UI 开发工具,能帮助开发人员独立创建组件,并在隔离的开发环境中以交互方式展示组件。 Storybook 是在主应用程序之外运行的,因此用户可以独立开发 UI 组件,而不必担心应用程序特定的依赖关系和要求。

Storybook 的基本思想是遵循 CDD + TDD 原则进行组件开发。

CDD 是 Component Drive Development,即以组件为基础自底而上的开发流程,每个组件都可作为一个独立的 story 进行开发。

TDD 是 Test Drive Development,在开发过程中对每个组件进行 UI 及功能上的测试。

本文介绍在 Vue 框架下的 App 中使用 Storybook 的方法。


以下图所示的 Taskbox App 为例。

Screen Shot 2020-03-09 at 16.34.56.png

Setup Vue Storybook

使用 Vue CLI 创建 App,并启用 Storybook 和 Jest 测试

# Create our application, using a preset that contains jest:
npx -p @vue/cli vue create taskbox --preset hichroma/vue-preset-learnstorybook

cd taskbox

# Add Storybook:
npx -p @storybook/cli sb init

可以使用以下命令检查 App 是否初始化成功:

# Run the test runner (Jest) in a terminal:
npm run test:unit

# Start the component explorer on port 6006:
npm run storybook

# Run the frontend app proper on port 8080:
npm run serve

创建组件的 Story

使用 TDD 的开发模式,在开发每个组件时为这个组件创建一个或多个 story,例如:

  • 组件:Task.vue
  • 组件的 story:Task.stories.js

上述 Taskbox 的最基础组件就是 Task,一个 Task 包含 title、state 两个属性,及 onPinTask、onArchiveTask 两个事件。Task 组件的 story 样例如下:

// src/components/Task.stories.js

// 使用stroybook提供的addon-actions库来mock事件的处理
import { action } from '@storybook/addon-actions';
// 引入组件
import Task from './Task.vue';

export default {
  // story的标题
  title: 'Task',
  // 排除不需要storybook渲染的文件
  excludeStories: /.*Data$/,
};

// mock事件处理
export const actionsData = {
  onPinTask: action('onPinTask'),
  onArchiveTask: action('onArchiveTask'),
};

// 创建测试数据
export const taskData = {
  id: '1',
  title: 'Test Task',
  state: 'Task_INBOX'
};

// Task组件为TASK_PINNED状态时的story
export const Pinned = () => ({
  components: { Task },
  template: ``,
  props: {
    task: {
      default: () => ({
        ...taskData,
        state: 'TASK_PINNED'
      })
    },
  },
  methods: actionsData,
});

// Task组件为其他状态的story
...

上述 story 样例里包含了几个关键点

  • 通过指定不同 props,我们可以创建不同状态下的组件,以测试组件 UI 是否符合预期
  • 我们只关心组件的行为是否能正确发生(如是否正确传参)而不关心后续的处理,因此我们可以将事件处理 mock 掉
  • 将 mock 的 action、测试数据等 export 出去可以便于我们在其他 story 中复用
  • 每一个 story 都是该组件的一个可视化测试,启动 storybook 后就可以看到对应的测试:
Screen Shot 2020-03-09 at 17.17.35.png

运行 story 所需的配置:

// .storybook/main.js
module.exports = {
  stories: ["../src/components/**/*.stories.js"],
  addons: ["@storybook/addon-actions", "@storybook/addon-links"]
};

如果希望 storybook 加载 app 引用的样式文件,还需要配置 preview.js

// .storybook/preview.js
import "../src/index.css";

Vuex 对组件 Story 的影响

当 App 里状态较多时我们会考虑使用 Vuex 管理组件间的数据流,但使用 Vuex 之后组件对外部数据的依赖性增加了,不便于隔离测试, 因此较好的实践是将引入的 Vuex 状态管理抽离成一个容器组件,例如:

  • 原组件:PureTask.vue

    
    
    
    
  • 容器组件:Task.vue

    
    
    
    

这样 PureTask 组件就是一个可隔离的独立组件,我们可以对它写 story 测试而不用额外考虑数据上下文。

此外,当我们用 Task 组件构建外层的 TaskList 组件时,由于自组件 Task 依赖 Vuex 提供的状态 store,父组件 TaskList 不再是一个可隔离的独立组件,需要在它的 story 里提供 Vuex store 以为 Task 组件提供必需的数据上下文。

//src/components/TaskList.stories.js
Vue.use(Vuex);
export const store = new Vuex.Store({
  state: {...},
  actions: {...},
});

export default {
  title: 'TaskList',
  excludeStories: /.*store$/,
};

export const Default = () => ({
  components: { TaskList },
  template: ``,
  store,
});

Snapshot Testing

快照测试是指记录给定输入的组件的“已知合格”输出,然后在将来输出发生变化时标记该组件的做法。 这样每次测试时可以看到我们对组件的哪部分进行了修改,当单元测试不通过时便于快速定位问题。

但在使用快照测试时应确保组件呈现不变的数据,以使快照测试不会每次都失败,例如是否存在日期或随机生成的值。

Setup

安装依赖

npm i -D @storybook/addon-storyshots jest-vue-preprocessor

创建 storybook 测试

// tests/unit/storybook.spec.js
import initStoryshots from "@storybook/addon-storyshots";

initStoryshots();

在 Jest 配置文件 jest.config.js 中增加以下配置:

transformIgnorePatterns: ["/node_modules/(?!(@storybook/.*\\.vue$))"]

运行测试

# 直接运行测试
npm run test:unit

# 运行测试并更新 snapshot
npm run test:unit -- -u

Tips

如果项目中引入了外部组件库,例如 Element-UI,为了处理外部组件库的样式文件,需要把样式文件 mock 掉。可以使用 ES6 Proxy mock CSS Modules:

npm i -D identity-obj-proxy

并在 Jest 配置文件 jest.config.js 中增加以下配置:

"moduleNameMapper": {
  "\\.(css|less)$": "identity-obj-proxy"
}

总结

Storybook 是一个 UI 开发工具库,它的核心思想是 CDD + TDD。使用 storybook 可以在隔离的开发环境中以交互方式展示独立的组件,方便对组件 UI 和行为进行测试;在团队开发中有助于保证组件 UI 的一致性,也可以避免大家重复造轮子。

参考

  • Storybook for Vue tutorial

你可能感兴趣的:(Stroybook的基本使用)