初探Vue单元测试

说是初探其实我也不知道尝试多少次了,但是每次也就看看那本《Testing Vue.js Applications》,书看了很多遍,真正操作起来就又是另一番光景了。

jest

从简单的node项目开始说起

npm init -y // 初始化一个node项目
npm i -D jest // 安装jest

接下来写一个简单的功能sayHello

// sayHello.js
module.exports = function(name) {
  return 'Hello, ' + name;
}

好了让我们测试一下这个函数

// sayHello.test.js
const sayHello = require('./sayHello.js');

test('should return Hello, John', () => {
  expect(sayHello('John')).toBe('Hello, John');
});

在jest中所有需要判断的内容都需要用expect函数包裹起来,然后就能
用jest提供了断言函数了,比如上例中的toBe。

配置文件

jest虽然号称零配置,还是提供了配置文件的jest init.

使用babel

我们已经被js新的语法给占领了,不使用babel是不可能的了

npm i -D babel-jest @babel/core @babel/preset-env

// babel.config.js
module.exports = {
  presets: [[
    '@babel/preset-env',
	{
	  targets: {
	    node: 'current'
	  }
	}
  ]]
};

默认情况下jest是会自动检测是否有babel.config.js的,如果有就会使用,所以不需要配置jest.config.js中的transform来告诉jest使用babel来处理js文件。安装jest的时候自动安装了babel-jest,所以这个不需要安装了,但是上面babel.config.js中使用了@babel/preset-env需要安装,同时它依赖@babel/core。因此要安装:

npm i -D @babel/core
npm i -D @babel/preset-env

总结一下就是安装jest是babel-jest已经有了,不过babel-jest只是胶水代码用来适配babel,而babel相关的的包需要自己安装,比如@babel/core和@babel/preset-env,如果你还用到了别的preset或plugin,也需要单独安装了。

使用ts

js在大型项目中使用简直是个灾难,你根本不知道传来传去的对象里面究竟有哪些字段,这时候多希望js是静态语言呀。typescript可以帮助我们。

npm i -D @babel/preset-typescript

// babel.config.js
module.exports = {
  presets: [[
    '@babel/preset-env',
	{
	  targets: {
	    node: 'current'
	  }
	}
  ], '@babel/preset-typescript']
};

However, there are some caveats to using TypeScript with Babel. Because TypeScript support in Babel is transpilation, Jest will not type-check your tests as they are run. If you want that, you can use ts-jest

这段话是从jest的文档中拷贝的

matcher

  • toBe
  • toEqual
  • toBeNull
  • toBeUndefined
  • toBeDefined
  • toBeTruthy
  • toBeFalsy
  • toBeGreaterThan
  • toBeGreaterThanOrEuqal
  • toBeLessThan
  • toBeLessThanOrEqual
  • toBeCloseTo
  • toMatch
  • toContain
  • toThrow

测试异步

  • done
  • promise expect().resolves expect().rejects
  • async/await

setup and teardown

  • beforeAll
  • afterAll
  • beforeEach
  • afterEach
  • describe

mock functions

jest.fn返回一个mock function

  • mockFunction.mock.calls是一个二维数组,保存了mockFunction被调用的参数
  • mockFunction.mock.results是一个数组,保存了每次调用的返回值
  • mockFunction.mock.instances是一个数组,记录每次调动时的context

我们还可以mock返回值

const myMock = jest.fn();
myMock.mockReturnValueOnce(10).mockReturnValueOnce('x').mockReturnValue(true);

名字上看很容易理解

  • mockReturnValueOnce
  • mockReturnValue

使用jest.mock来mock模块


还有mockImplementation和mockImplementationOnce

mockReturnThis

开始Vue项目的单元测试了

好了,到此为止jest大概了解了。言归正传,还是回到vue的单元测试吧。

npm i -g vue-cli // vue的脚手架工具
vue init webpack vue-unit // 创建一个vue的项目,选择jest作为单元测试的框架

使用vue脚手架工具创建出来的项目已经帮我们配置好了jest。不过还是有必要了解一下jest的配置。让我们先看一下创建好的项目是怎么配置的。

scripts

先看package.json中的unit

jest --config test/unit/jest.conf.js --coverage

它指定了jest的配置文件,在test/unit/jest.conf.js,并且还有代码覆盖率

test/unit/jest.conf.js

实话说,这个文件中的东西有点超出了我的知识范围了。刚刚看的jest文档也没看到这些东西,一定是漏看了

const path = require('path')

module.exports = {
  rootDir: path.resolve(__dirname, '../../'),
  moduleFileExtensions: [
    'js',
    'json',
    'vue'
  ],
  moduleNameMapper: {
    '^@/(.*)$': '/src/$1'
  },
  transform: {
    '^.+\\.js$': '/node_modules/babel-jest',
    '.*\\.(vue)$': '/node_modules/vue-jest'
  },
  testPathIgnorePatterns: [
    '/test/e2e'
  ],
  snapshotSerializers: ['/node_modules/jest-serializer-vue'],
  setupFiles: ['/test/unit/setup'],
  mapCoverage: true,
  coverageDirectory: '/test/unit/coverage',
  collectCoverageFrom: [
    'src/**/*.{js,vue}',
    '!src/main.js',
    '!src/router/index.js',
    '!**/node_modules/**'
  ]
}

这是具体的配置文件的内容https://jestjs.io/docs/en/configuration

报错

创建好的vue项目,运行npm run unit会报错

SecurityError: localStorage is not available for opaque origins

真是一个伤感的事情,解决办法是在jest.conf.js中添加
testURL: 'http://localhost’亲测可用

jest的文档里说testURL默认值是http://localhost,按道理来说不应该报错的呀。真是搞不懂。还有搞不懂的是真是刚使用脚手架创建的vue项目,怎么会这样呢?

算了,搞不懂就先放下吧。

看看package.json都是用了哪些跟jest相关的包

  • jest
  • babel-jest
  • vue-jest
  • jest-seriealizer-vue 用来做snapshot测试的

e2e

npm run e2e报错了

Error retrieving a new session from the selenium server

Connection refused! Is selenium server started?
{
  value: {
    message: 'session not created: Chrome version must be between 71 and 75\n' +
      '  (Driver info: chromedriver=2.46.628402 (536cd7adbad73a3783fdc2cab92ab2ba7ec361e1),platform=Windows NT 10.0.15063 x86_64) (WARNING: The server did not provide any stacktrace information)\n' +
      'Command duration or timeout: 997 milliseconds\n' +
      "Build info: version: '3.141.59', revision: 'e82be7d358', time: '2018-11-14T08:25:53'\n" +
      "System info: host: 'DESKTOP-TK2R2R0', ip: '192.168.56.1', os.name: 'Windows 10', os.arch: 'x86', os.version: '10.0', java.version: '1.8.0_181'\n" +
      'Driver info: driver.version: unknown',
    error: 'session not created'
  },
  status: 33
}

表面上看来应该是我的chrome的版本太高了,我的是80.0.3987.149的

解决方案很简单,找到符合自己电脑上chrome版本的chromedriver就好了。官网估计一般人都上不去,我到淘宝镜像上下载的,地址是https://npm.taobao.org/mirrors/chromedriver/,这是一个列表选择自己需要的就好了。我看了看我本机上装的是2.46.628402,这个chromedriver也太老了吧。把下载的文件解压,里面只有一个chromedriver.exe,把这个东西拷贝到项目的node_modules/chromedriver/lib目录下,把原来的那个覆盖掉就行了。再次运行,成功了

vue test utils

vue单元测试使用vue test utils来进行。

如果你创建vue项目时没有指定测试。那么可以用

# unit testing
vue add @vue/unit-jest

# or:
vue add @vue/unit-mocha

# end-to-end
vue add @vue/e2e-cypress

# or:
vue add @vue/e2e-nightwatch

我猜vue add是新脚手架的功能,我的脚手架没有这个功能。

真的是让人难过,使用脚手架创建的项目,竟然没有引入@vue/test-utils,这个不是号称vue官方提供的测试工具吗?自己手动安装吧

npm i -D @vue/test-utils

使用tdd写一个组件

先定一下组件Item的规格说明

  • 显示name字段
  • 显示desc字段
  • name字段使用a标签包括,href是url字段

创建Item.vue和Item.spec.js

// src/components/Item.vue
<template>
	<div></div>
</template>
<script>
export default {
	name: 'Item',
	data() {
		return {};
	}
}
</script>
// unit/Item.spec.js
import Item from '@/components/Item.vue';
import { shallowMount } from '@vue/test-utils';

describe('test Item', () => {
	const propsData = {
		name: '岁月神偷',
		url: 'http://www.baidu.com?word=岁月神偷',
		desc: '一部描述时间流逝的电影,发人深思'
	};
	const wrapper = shallowMount(Item, {
		propsData
	});
	it('should contain name', () => {
        expect(wrapper.text()).toContain(propsData.name);
    });

    it('should contain desc', () => {
        expect(wrapper.text()).toContain(propsData.desc);
    });

    it('should contain an anchor', () => {
        expect(wrapper.contains('a')).toBe(true);
    });
	it('the anchor\'s href should be the url field', () => {
        expect(wrapper.find('a').attributes().href).toBe(propsData.url);
    });
});

首次运行npm run unit,报错,毫无疑问,这是tdd的第一步red。
第二步写最简单的代码让测试过。

// src/components/Item.vue
<template>
	<div>
		<a :href="url">{{ name }}</a> {{ desc }}
	</div>
</template>
<script>
	export default {
		name: 'Item',
		props: ['name', 'url', 'desc'],
		data() {
			return {};
		}
	}
</script>

再次运行npm run unit,成功了。单元测试只进行逻辑的测试,至于样式的测试还是靠自己手动测试了。

至于逻辑复杂的情况怎么进行测试,我还要继续学习。

有用点赞哦,嘻嘻:)

你可能感兴趣的:(前端之路)