手记

【九月打卡】第5天. TDD 开发的更多特性

课程名称:web前端架构师

课程章节:第11周 第五章

主讲老师:张轩

课程内容:通过完善文件上传组件,TDD 开发的更多特性

文件上传列表

之前已经实现了文件上传的功能,但是体验不好。参考 antd 文件上传组件的实现 https://ant.design/components/upload-cn/

需要下面几个功能

  • 展示文件上传列表,每次上传列表就会增加一项
  • 文件上传过程中
    • 展示上传中loading,
    • 上传过程中按钮不可点击
  • 上传成功后展示文件上传成功
  • 上传失败后展示文件上传失败
  • 点击上传文件右侧的删除按钮,删除该文件并从列表中移除掉

然后,根据上面所列的功能,编写对应的单元测试,实现过程很简单

上传成功

it('uplaod success', async () => {
   ...
   await wrapper.get('input').trigger('change')
   expect(request).toHaveBeenCalledTimes(1)
   // 上传中
   expect(wrapper.get('button').attributes('disabled')).toBeTruthy()
   expect(wrapper.findAll('li').length).toBe(1)
   const firstChiild = wrapper.get('li:first-child')
   expect(firstChiild.classes()).toContain('upload-loading')
   await flushPromises()
   // 上传成功
   expect(firstChiild.classes()).toContain('upload-success')
   expect(firstChiild.get('.firstname').text()).toBe(testFile.name)
})

下面是上传失败


  it('uplaod fail', async () => {
    request.mockRejectedValueOnce({ err: 'err' })
    await wrapper.get('input').trigger('change')
    expect(request).toHaveBeenCalledTimes(2)
    await flushPromises()
    expect(wrapper.findAll('li').length).toBe(2)
    const lastChild = wrapper.get('li:last-child')
    expect(lastChild.classes()).toContain('uplaad-fail')
    await lastChild.get('.delete-icon').trigger('click')
    expect(wrapper.findAll('li').length).toBe(1)
  })

然后根据测试case, 一项一项编写代码,跑通测试代码

在这个过程中,不需要关注在浏览器会怎样,只需要让测试一项一项跑通即可

<script setup lang="ts">
import { reactive, ref, computed } from 'vue'
import request from '@/utils/request'
type UploadStatus = 'ready' | 'loading' | 'success' | 'fail'
interface FileItem {
  id: string
  name: string
  size: number
  status:UploadStatus
  raw: File
}
const fileRef = ref<HTMLInputElement | null>(null)
// const uploadStatus = ref<UploadStatus>('ready')
const uploadFiles = reactive<FileItem[]>([])
const isUploading = computed(() => uploadFiles.some(file => file.status === 'loading'))

async function uploadFile (e: Event) {
  const target = e.target as HTMLInputElement
  const files = target.files
  if (files) {
    const uploadFile = files[0]
    const formData = new FormData()
    formData.append(target.name, uploadFile)
    const fileObj = reactive<FileItem>({
      id: '' + Date.now(),
      name: uploadFile.name,
      size: uploadFile.size,
      status: 'loading',
      raw: uploadFile
    })
    uploadFiles.push(fileObj)
    try {
      fileObj.status = 'loading'
      const res = await request(formData)
      fileObj.status = 'success'
      console.log(res)
    } catch (e) {
      fileObj.status = 'fail'
    } finally {
      (fileRef.value as HTMLIN).value = ''
    }
  }
}
function triggerUpload () {
  fileRef.value?.click()
}
function delFile (index: number) {
  uploadFiles.splice(index, 1)
}
</script>

<template>
  <input
    type="file"
    name="file"
    ref="fileRef"
    :style="{display: 'none'}"
    @change="uploadFile"
  >
  <button
    @click="triggerUpload"
    :disabled="isUploading"
  >
    <span v-if="isUploading">正在上传</span>
    <span v-else>点击上传</span>
  </button>
  <ul>
    <li
      v-for="(file,index) in uploadFiles"
      :key="file.id"
      :class="'upload-'+ file.status"
    >
      <span class="filename">{{ file.name }}</span>
      <button
        class="delete-icon"
        @click="delFile(index)"
      >
        del
      </button>
    </li>
  </ul>
</template>

通过上面的开发,我们发现很大部分时间都是在写测试,但是写完测试后,编写代码就会编写很容易

根据一项一项测试编写代码,可以使我们在开发过程中的目标比较清晰,而且不需要频繁的在浏览器进行复杂的操作,就可以测试功能是否正常,编写代码报错自动就会完成测试,出现问题也可以很好的定位。

所以编写测试代码未必会浪费时间。再次感受到了 TDD 真的是非常好用

0人推荐
随时随地看视频
慕课网APP