vue 测试

vue测试主要依赖 jest ,Vue Test Utils和Cypress,jest主要测试工具函数,Vue Test Utils是组件测试,cypress是测业务。

Jest

安装相关依赖项,

vue add @vue/unit-jest 

yarn add vue-jest babel-jest -D

ps:vscode里还需要安装jest插件


对jest.config.js文件进行相关配置,同时设置eslintrc.js

testMatch看情况自定义

module.exports = {
    preset: '@vue/cli-plugin-unit-jest/presets/typescript-and-babel',
    transform: {
        '^.+\\\\.js$': 'babel-jest',
        '^.+\\\\.vue$': 'vue-jest'
    },
    collectCoverage: true,
    // coverageProvider: 'v8',
    moduleFileExtensions: [
        'js',
        'json',
        'jsx',
        'ts',
        'tsx',
        'node',
        'vue'
    ],
    testMatch: ['**/tests/**/*.[jt]s?(x)', '**/?(*.)+(spec|test).[jt]s?(x)'] 
}

env: {
        browser: true,
        node: true,
        es6: true,
        jest: true
  },


Jest的基本使用方法

describe与it(test)基本用法,以及一些常用的expect用法

import { isString } from '@/utils/is'

describe('isString', () => {

    it('test isString arg is defined', () => {
        expect(isString).toBeDefined()
    })

    it('test isString arg is string', () => {
        expect(isString('luanhanxiaodahaoren')).toBe(true)
    })

    // expect(..).toBeDefined() 
    // expect(..).toBe(..) 
    // expect(..).toEqual(..) 
    // expect(..).toBeInstanceOf(...)
    // expect(..).toBeNull()
})


beforeEach,afterEach与beforeAll ,afterAll的区别

在指定的作用域范围(describe或者js文件)内,beforeEach与afterEach在每个it执行之前都会运行一次,afterAll与beforeAll只会在所有的it之前和之后执行一次。


mock函数的使用

const mockCallback = jest.fn(x => 42 + x);
forEach([0, 1], mockCallback);

// The mock function is called twice
expect(mockCallback.mock.calls.length).toBe(2);

// The first argument of the first call to the function was 0
expect(mockCallback.mock.calls[0][0]).toBe(0);

// The first argument of the second call to the function was 1
expect(mockCallback.mock.calls[1][0]).toBe(1);

// The return value of the first call to the function was 42
expect(mockCallback.mock.results[0].value).toBe(42);

expect(mockCallback).toHaveBeenCalledTimes(2);

基本在工作主要基本结合定时器进行使用


mock对象属性的几种方法

// 1、推荐使用的,jest.spyOn() mockImplementation(),
describe('setDocumentTitle when client is Iphone', () => {
    let navigatorSpy: any
    beforeAll(() => {
        const { navigator } = window
        navigatorSpy = jest.spyOn(window, 'navigator', 'get')
        navigatorSpy.mockImplementation(() => ({ ...navigator, userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 7_1_2 like Mac OS X) > AppleWebKit/537.51.2 (KHTML, like Gecko) Mobile/11D257 > MicroMessenger/6.0.1 NetType/WIFI' }))
    })

    afterAll(() => {
        navigatorSpy.mockRestore()
    })

    it('title is haha', () => {
        setTitle('haha')
        expect(document.title).toBe('haha')
    })
})

// 2、使用Object.defineProperty进行修改属性
Object.defineProperty(window, 'location', {
      get() {
        return { href: 'www.baidu.com' };
      },
    });


mock时间

mock定时器

describe('useThrottle', () => {
    beforeAll(() => {
        jest.useFakeTimers()
    })
    afterAll(() => {
        jest.useRealTimers()
    })
    afterEach(() => {
        jest.clearAllTimers()
    })
    it('should excute immediately', () => {
        const [mockFn, fn] = getHook(90, { immediate: true })
        fn()
        expect(mockFn).toHaveBeenCalledTimes(1)
        jest.advanceTimersByTime(60)
        fn()
        expect(mockFn).toHaveBeenCalledTimes(1)
    })
})

mock指定时间

import { getPastRange, setPickedTime } from '@/utils/day'
// eslint-disable-next-line @typescript-eslint/no-var-requires
const mockDate = require('mockdate')

beforeAll(() => {
    // window.Date.now = jest.fn(() => new Date('2019-04-07T10:20:30Z').getTime())
    mockDate.set('2021-01-15')
})

afterAll(() => {
    mockDate.reset()
})

describe('get the past time period', () => {
    it('get the past time period', () => {
        expect(getPastRange(0)).toEqual(['2021-01-14', '2021-01-14'])
        expect(getPastRange(6)).toEqual(['2021-01-08', '2021-01-14'])
        expect(getPastRange(29)).toEqual(['2020-12-16', '2021-01-14'])
    })

    it('set picked time ', () => {
        const mockTime = ['', '']
        setPickedTime(0, mockTime)
        expect(mockTime).toEqual(['2021-01-14', '2021-01-14'])
        setPickedTime(6, mockTime)
        expect(mockTime).toEqual(['2021-01-08', '2021-01-14'])
        setPickedTime(29, mockTime)
        expect(mockTime).toEqual(['2020-12-16', '2021-01-14'])
    })
})

mock 模块

import { openWindow } from '@/utils/utils'
jest.mock('@/utils/utils', () => ({
    openWindow: jest.fn()
}))
describe('test downloadByUrl with params', () => {
    it('test downloadByUrl with params', () => {
        downloadByUrl({ url: '/v1/admin', params: { user: 'luanhanxiao', nickname: 'dahaoren' }})
        expect((openWindow as jest.Mock).mock.calls[1][0]).toBe('<https://beta-admin.sdbattery.net/v1/admin?user=luanhanxiao&nickname=dahaoren>')
    })
})

cypress

为什么需要?

去自动点击一个真实浏览器环境中的页面,再通过直接抓取页面上的DOM来断言是否符合预期,这是最接近用户的测试方式,所以我认为从一定程度而言端到端测试对于一个产品的发布起到了至关重要的作用

1.安装 命令

vue add @vue/e2e-cypress

2.目录结构分析 Cypress 允许配置 package.json 文件的 scripts 字段,来定义打开方式 首先,进入 Cypress安装目录 ,打开 package.json 在 scripts 下,添加

https://upload-images.jianshu.io/upload_images/19830076-bbf256d65e4f22d0.png?imageMogr2/auto-orient/strip|imageView2/2/w/1240

在使用 cypress open 命令首次打开 Cypress,Cypress 会自动进行初始化配置并生成一个默认的文件夹结构

如果发现端口号被占用,可以杀掉进程;

image.png
https://upload-images.jianshu.io/upload_images/19830076-67209b2c0985a14c.png?imageMogr2/auto-orient/strip|imageView2/2/w/1240
image.png

首先在启动整个Cypress时,会在项目的根目录中去寻找这个文件,

image.png

pluginsFile这个配置项指向了我们重新配置的路径,在这个被指向的配置文件中再去使用config参数配置其他目录所在的位置

image.png

当然我们也可以直接在cypress.json中去指定这些配置

(1) integration,测试文件就是测试用例 (2) plugins 插件文件,使你可以修改或扩展 Cypress 的内部行为 (3) support 每个测试文件运行之前,Cypress 都会自动加载支持文件 cypress/support/index.js eg: 只需要在 cypress/support/index.js 文件里添加 这将能实现每次测试运行前打印出所有的环境变量信息

beforeEach(() => {
    cy.log(`当前环境变量为${JSON.stringify(Cypress.env()
    )}`)
})

https://upload-images.jianshu.io/upload_images/19830076-b215d6b8ec7afbf4.png?imageMogr2/auto-orient/strip|imageView2/2/w/1240

3.自定义cypress, 可在

cypress/support/commands.js

编写 eg:

// 全局定义获取token
Cypress.Commands.add('token', () => {
})

4.开始测试

  • 1 查找元素方式 get()
// 通过元素的 id 属性来定位
cy.get("#header").click()
// 通过元素的 class 属性来定位
cy.get(".btn").click()

  • 2 点击 click()
// 先获取 DOM 元素,再对 DOM 元素操作
// { force: true } 强制点击,和所有后续事件
cy.get('.el-date-editor').click({ force: true })

  • 3 输入内容 type()
// 在 DOM 元素中输入内容
//  先获取 DOM 元素,再对 DOM 元素进行 type 操作
 cy.get('.el-date-editor input[placeholder=结束时间]').clear().type('2021-08-29')

  • 4 清空输入框的所有内容
// 需要先拿到 DOM 元素,且是 <input> 或 <textarea > 标签,再执行 clear() 操作
cy.get('input').type('Hi!').clean()

  • 5 单选框或复选框,达到选中的作用
// 选中指定值的选项
.check(value)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,451评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,172评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,782评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,709评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,733评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,578评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,320评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,241评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,686评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,878评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,992评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,715评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,336评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,912评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,040评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,173评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,947评论 2 355

推荐阅读更多精彩内容