【架构师(第三十一篇)】前端测试之 TDD 的开发方式

1,965 阅读2分钟

什么是 TDD 的开发方式

TDD(Test-Driven Development) 测试驱动开发

  • 先根据需求写测试用例
  • 测试用例全部是失败的状态
  • 开始写代码实现功能
  • 将所有的测试用例由失败调为成功状态

以 TDD 的思想开发一个颜色选择器组件

需求分析

显示

  • 左侧显示当前颜色
  • 右侧显示十种常用的颜色
  • 右侧最后一个是透明,点击可以清除颜色效果

点击

  • 点击左侧,显示颜色选择器弹框,在颜色选择框中点击,或者修改 input 中的值后,将新的值以事件的形式发射出去。
  • 点击右侧的颜色,将新的值以事件的形式发射出去,左侧修改为点击的颜色。

测试用例编写

import type { VueWrapper } from '@vue/test-utils';
import { mount } from '@vue/test-utils';
import ColorPicker from '@/components/ColorPicker.vue';
import rgbHex from 'rgb-hex';

const defaultColors = [
  '#ffffff',
  '#f5222d',
  '#fa541c',
  '#fadb14',
  '#52c41a',
  '#1890ff',
  '#722ed1',
  '#8c8c8c',
  '#000000',
  '',
];

// 定义 wrapper
let wrapper: VueWrapper<any>;

// 测试 UserProfile.vue
describe('UserProfile.vue', () => {
  beforeAll(() => {
    // 获取组件
    wrapper = mount(ColorPicker, {
      // 传入到组件内部的属性
      props: { value: '#ffffff' },
    });
  });
  // 测试页面是否出现了正确的结构
  it('should render the current interface', async () => {
    // 测试左侧是否为 input , 类型和值是否正确
    expect(wrapper.find('input').exists()).toBeTruthy();
    const input = wrapper.get('input').element;
    expect(input.type).toBe('color');
    expect(input.value).toBe('#ffffff');
    // 测试右侧是否有颜色的列表
    expect(wrapper.findAll('.picked-color-list li').length).toBe(
      defaultColors.length,
    );
    // 检查一个元素的 css backgroundColor 属性是否等于对应的颜色
    const firstItem = wrapper.get('li:first-child div').element as HTMLElement;
        expect('#' + rgbHex(firstItem.style.backgroundColor)).toBe(
      defaultColors[0],
    );
    // 测试最后一个元素是否有特殊的类名
    const lastItem = wrapper.get('li:last-child div').element as HTMLElement;
    expect(lastItem.classList.contains('transparnet-back ')).toBeTruthy();
  });
  // 测试 input 修改以后,是否发送对应的事件和对应的值
  it('should render username  when login is true', async () => {
    const blackHex = '#000000';
    // 获取 input
    const input = wrapper.get('input');
    // 修改 input 的值
    await input.setValue(blackHex);
    // 是否发射了事件
    expect(wrapper.emitted()).toHaveProperty('change');
    const events = wrapper.emitted('change')!;
    // 发射事件的参数是否正确
    expect(events[0]).toEqual([blackHex]);
  });
  // 测试点击右侧颜色列表以后,是否发送对应的值
  it('should call logout and show message,call router.push after timeout', async () => {
    const firstItem = wrapper.get('li:first-child div');
    // 触发点击事件
    firstItem.trigger('click');
    // 是否发射了事件
    expect(wrapper.emitted()).toHaveProperty('change');
    const events = wrapper.emitted('change')!;
    // 发射事件的参数是否正确
    expect(events[1]).toEqual([defaultColors[0]]);
  });
});

目前所有的测试用例都是失败的,接下来我们就通过编码让测试用例通过。

image.png

代码编写

<template>
  <div class="lego-color-picker">
    <div class="native-color-container">
      <input type="color"
             :value="value"
             @input="onChange($event.target.value)">
    </div>
    <ul class="picked-color-list">
      <li v-for="(item, key) in colors"
          :key="key"
          @click.prevent="onChange(item)">
        <div :style="{ backgroundColor: item }"
             class="color-item"
             v-if="item.startsWith('#')"></div>
        <div v-else
             class="color-item transparnet-back"></div>
      </li>
    </ul>
  </div>
</template>

<script lang="ts">
import { defineComponent, PropType } from 'vue'
const defaultColors = ['#ffffff', '#f5222d', '#fa541c', '#fadb14', '#52c41a', '#1890ff', '#722ed1', '#8c8c8c', '#000000', '']
export default defineComponent({
  props: {
    value: {
      type: String
    },
    colors: {
      type: Array as PropType<string[]>,
      default: defaultColors
    }
  },
  emits: ['change'],
  setup(props, context) {
    const onChange = (color: string) => {
     // 触发事件 
      context.emit('change', color)
    }
    return {
      onChange
    }
  }
})
</script>

<style>
.lego-color-picker {
  display: flex;
}

.native-color-container {
  width: 40%;
}

.native-color-container input[type="color"] {
  width: 100%;
  cursor: pointer;
  height: 50px;
  border: 0;
  padding: 0;
  background-color: transparent;
}

.picked-color-list {
  padding: 0 0 0 5px;
  margin: 0;
  width: 60%;
  display: flex;
  list-style-type: none;
  flex-wrap: wrap;
  justify-content: space-between;
}

.picked-color-list li {
  flex: 1;
  width: 20%;
  min-width: 20%;
  max-width: 20%;
}

.color-item {
  padding: 3px;
  width: 20px;
  height: 20px;
  border-radius: 3px;
  margin-right: 5px;
  cursor: pointer;
  border: 1px solid #ccc;
}

.transparnet-back {
  background: transparent;
}
</style>

现在的测试用例已经全部通过了,大功告成。

image.png