前端自动化测试概览【超全面介绍】包你学废
为什么需要自动化测试
项目经过不断的开发,最终肯定会趋于稳定,在适当的时机下引入自动化测试能及早发现问题,保证产品的质量。
自动化的收益 = 迭代次数 * 全手动执行成本 - 首次自动化成本 - 维护次数 * 维护成本
测试
测试作为完整的开发流程中最后的一环,是保证产品质量重要的一环。而前端测试一般在产品开发流程中属于偏后的环节,在整个开发架构中属于较高层次,前端测试更加偏向于GUI的特性,因此前端的测试难度很大。
测试方法
黑盒测试
黑盒测试一般也被称为功能测试,黑盒测试要求测试人员将程序看作一个整体,不考虑其内部结构和特性,只是按照期望验证程序是否能正常工作。黑盒测试更接近用户使用的真实场景,因为对于用户来说,程序的内部是不可见的。
以下是黑盒测试中常用的测试方法:
等价类划分
等价类划分主要是在已有输入规则的情况下,确定合法输入与非法输入区间来设计测试用例
如:如网站登录密码必须由6位数字构成
有效等价类:6位数字
无效等价类:位数>6,位数<6,全角数字,字母、特殊字符等……
边界值分析
顾名思义,主要是根据输入输出范围的边界值进行测试用例的设计。原因是大量的错误往往发生在输入或输出范围的边界上(程序员往往在容易在这些地方犯错),边界值分析一般结合等价类划分进行使用,等价类划分区间边界一般就是边界值。
如:如网站登录密码长度必须为6-12位
有效等价类:位数[6-12]
无效等价类:位数<6 位数>12
边界值:6 12
错误推测、异常分析等
黑盒测试还包含一些其他的测试方式,由于测试往往是不可穷举性的,因此如何如何设计测试用例保证测试覆盖尽可能多的场景,不仅仅是依靠这些总结出来的方法,也考验测试人员自身的天赋。
白盒测试
白盒测试是基于代码本身的测试,一般指对代码逻辑结构的测试。白盒测试是在了解代码结构的前提下进行的测试,目的是遍历尽可能多的可执行路径,得出测试数据。白盒测试方法比较多,主要是逻辑覆盖,即检查代码的每一行、每一次判断结果。
逻辑覆盖方式从发现错误能力上排序主要有以下几种:
语句覆盖 (让程序执行到每一行语句)
判定覆盖 (让每一个判断语句满足真假)
条件覆盖 (让每一个判断语句里面的每一个条件都取到真假值)
判定/条件覆盖 (同时满足2和3)
条件组合覆盖 (判断语句中条件的每种组合至少出现一次)
路径覆盖 (覆盖程序每一条执行路径)
测试分类
按照软件工程自底而上的概念,前端测试一般分为单元测试(Unit Testing )、集成测试(Integration Testing)和端到端测试(E2E Testing)。从下面的图可以看出,从底向上测试的复杂度将不断提高,另一方面测试的收益反而不断降低的。
前端测试模型
单元测试(Unit Testing)
单元测试是指对程序中最小可测试单元进行的测试,一般而言是指对函数进行的测试。单元测试混合了编程和测试,由于是对代码内部逻辑进行测试,因此更多的使用白盒的测试方式。单元测试能强迫开发者写出更可测试的代码,一般而言这样的代码可读性也会高很多,同时良好的单元测试可以作为被测代码的文档使用。
函数可测性:可测试性高的函数一般而言是纯函数,即输入输出可预测的函数。即在函数内部不修改传入参数,不执行API请求或者IO请求,不调用其他非纯函数如Math.random()等
单元测试最大的特点是测试对象的细颗粒度性,即被测对象独立性高、复杂度低。
前端单元测试
前端单元测试和后端单元测试最大的区别在于,前端单元测试无法避免的会存在兼容性问题,如调用浏览器兼容性API,以及对BOM(浏览器对象模型)API的调用,因此前端单元测试需要运行在(伪)浏览器环境下。
就测试运行环境分类,主要有以下几种测试方案:
基于JSDOM
优点:快,执行速度最快,因为不需要浏览器启动
缺点:无法测试如seesion或cookie等相关操作,并且由于不是真实浏览器环境因此无法保证一些如Dom相关和BOM相关的操作的正确性,并且JSDOM未实现localStorage,如果需要进行覆盖,只能使用第三方库如node-localStorage (这个库本身对与执行环境的判断有一些问题)进行模拟。
基于PhantomJs等无头浏览器
优点: 相对较快,并且具有真实的DOM环境
缺点: 同样不在真实浏览器中运行,难以调试,并且项目issue非常多,puppeteer发布后作者宣布不再维护
使用Karma或puppeteer等工具,调用真实的浏览器环境进行测试
优点:配置简单,能在真实的浏览器中运行测试,并且karma能将测试代码在多个浏览器中运行,同时方便调试
缺点: 唯一的缺点就是相对前两者运行稍慢,但是在单元测试可接受范围内
前端单元测试工具
前端在近几年如雨后春笋一般涌现出非常多的测试框架和相关工具。
测试平台
karma – Google Angular团队开发的测试运行平台,配置简单灵活,能够很方便将测试在多个真实浏览器中运行。
测试框架
mocha – Tj大神开发的很优秀的测试框架,有完善的生态系统,简单的测试组织方式,不对断言库和工具做任何限制,非常灵活。
jasmine – 和Mocha语法非常相似,最大的差别是提供了自建的断言和Spy和Stub
jest – facebook出品的大而全的测试框架,React官方推荐的单元测试框架,配置简单运行速度快。(备注:无法和Karma进行集成)
AVA – 和上面的测试框架最大的区别在于多线程,运行速度更快。
其他 – 还有一些其他的前端测试框架,但是相似度比较高,无非是对断言和测试桩等工具的集成度不同,如果考虑稳定以及成熟度建议选择Mocha,对测试运行速度有非常高的要求可以考虑jest和AVA
测试辅助工具
断言库 – Chai 如果单元测试不跑在真实的浏览器环境中,可以简单使用node的assert,但是建议使用Chai作为断言库(提供了TDD和BDD风格多种断言方式,并且生态系统繁荣)。
测试桩(又称为测试替身) – Sinon、testDouble等工具提供了如测试桩、拦截模拟请求、”时间旅行“等功能,主要用于解决”函数不纯”(比如测试回调是否被正确调用,XHR是否正确发起请求,时间延迟后函数行为是否正确)的测试问题。
测试覆盖率工具
istanbul istanbul的基础实现,提供了命令行等工具,但是无法解决代码编译打点的问题
istanbul-instrumenter-loader istanbul的Webpack插件,能解决代码编译打点和测试报告输出的问题
其他参考
chai-as-promise 扩展Chai在Promise上的断言功能
sinon-chai 扩展Chai搭配Sinon时的断言功能
chai-jquery 扩展Chai在UI测试中的断言
istanbul官网 介绍了istanbul如何与多中测试框架集成以及对于Typescript等语言的支持
阮一峰-代码覆盖率工具 Istanbul 入门教程 介绍了代码覆盖率相关的概念以及Istanbul搭配Mocha的简单使用
常见单元测试栗子
在框架选型的时候考虑到以下条件或问题:
测试需要在真实浏览器中运行
测试执行需要足够迅速
被测代码为Typescript,因此使用需要解决编译和打点的问题
方便持续集成
最后选择使用Karma+Webpack+Mocha+Chai+Sion+istanbul-instrumenter-loader的解决方案。
项目结构:
karma可以很方便的与Webpack进行集成,只需要指定需要预编译的文件和使用的编译工具即可。
karma.conf.js配置Webpack编译
module.export=funciton(config){
config.set({frameworks: ['mocha', 'chai'],files: ['tests/setup.js'],preprocessors: {'tests/setup.js': ['webpack']},webpack: {resolve: {extensions: ['.js','.ts'],},module: {rules: [{test: /\.ts$/,loader: 'ts-loader',query: {transpileOnly: true}},{test: /\.ts$/,exclude: /(node_modules|libs|\.test\.ts$)/,loader: 'istanbul-instrumenter-loader',enforce: 'post',options: {esModules: true,produceSourceMap: true}}]},devtool: 'inline-source-map',},// karma-server support ts/tsx mime mime: {'text/x-typescript': ['ts', 'tsx']},
})
}
通过上面的配置中需要注意几个地方:
setup.js
// 导入所有测试用例
const testsContext = require.context("../src", true, /\.test\.ts$/);
testsContext.keys().forEach(testsContext);
使用webpack提供的require.context()将所有测试文件统一导入,然后karma将setup.js作为入口调用webpack进行编译,然后测试执行。
mime配置对于ts/tsx的支持
istanbul-instrumenter-loader loader调用顺序必须在ts或babel等编译工具编译之前否则将不准确,排除测试文件本身和依赖库覆盖率计算
使用enforce: ‘post’,确保loader调用顺序使用exclude排除第三方库和测试文件本身覆盖率计算
其他配置请参考karma和webpack,以及相关插件的文档
纯函数测试
/*** 验证邮箱* @params input 输入值* @return 返回是否是邮箱*/
export function mail(input: any): boolean {return /^[\w\-]+(\.[\w\-]+)*@[\w\-]+(\.[\w\-]+)+$/.test(input);
}/* ↑↑↑↑被测函数↑↑↑↑ */
/* ↓↓↓↓测试代码↓↓↓↓ */describe('验证邮箱#mail', () => {it('传入邮箱字符串,返回true', () => {expect(mail('abc@123.com')).to.be.true;expect(mail('abc@123.com.cn')).to.be.true;expect(mail('a-b-c@123.com.cn')).to.be.true;expect(mail('a_b_c@123.com.cn')).to.be.true;expect(mail('a_b_c@123-4-5-6.com.cn')).to.be.true;});it('传入非邮箱字符串,返回true', () => {expect(mail('')).to.be.false;expect(mail('abc@')).to.be.false;expect(mail('@123.com')).to.be.false;expect(mail('abc@123')).to.be.false;expect(mail('123.com')).to.be.false;});
});
Promise或其他异步操作的测试
Mocha支持异步测试,主要有下面三种方式:
使用async await
it(async ()=>{
await asynchronous()
// 一些断言
})
使用回调函数
it(done=>{
Promise.resolve().then(()=>{// 断言done() // 测试结束后调用回调标识测试结束
})
})
返回一个Promise
it(()=>{
// 直接返回一个Promise,Mocha会自动等待Promise resolve
return Promise.resolve().then(()=>{// 断言
})
})
含HTTP请求的测试
function http(method,url,body){// 使用XHR发送ajax请求
}/* ↑↑↑↑被测函数↑↑↑↑ */
/* ↓↓↓↓测试代码↓↓↓↓ */it('method为GET', (done) => {const xhr=fake.useFakeXMLHttpRequest()const request = []xhr.onCreate = xhr => {requests.push(xhr);};requests[0].respond(200, { "Content-Type": "application/json" },'[{ "id": 12, "comment": "Hey there" }]');get('/uri').then(() => {xhr.restore();done();});expect(request[0].method).equal('GET');
})
或者封装一下fakeXMLHttpRequest
/*** 使用fakeXMLHttpRequest* @param callback 回调函数,接受请求对象作为参数*/
export function useFakeXHR(callback) {const fakeXHR = useFakeXMLHttpRequest();const requests: Array<any> = []; // requests会将引用传递给callback,因此使用const,避免指针被改写。fakeXHR.onCreate = request => requests.push(request);callback(requests, fakeXHR.restore.bind(fakeXHR));
}it('method为GET', (done) => {useFakeXHR((request, restore) => {get('/uri').then(() => {restore();done();});expect(request[0].method).equal('GET');request[0].respond();})
})
useFakeXHR封装了对Sinon的useFakeXMLHttpRequest,实现对XHR请求的fake
Fake XHR and server - Sinon.JS 官方文档参考
Fake XHR和 Fake server的区别:后者是对前者的更高层次的封装,并且Fake server的颗粒度更大
时间相关的测试
// 延迟delay毫秒后调用callback
function timer(delay,callback){setTimeout(callback,delay);
}/* ↑↑↑↑被测函数↑↑↑↑ */
/* ↓↓↓↓测试代码↓↓↓↓ */it('timer',()=>{const clock=sinon.useFakeTimers()const spy=sinon.spy() // 测试替身timer(1000,spy) clock.tick(1000) // "时间旅行,去到1000ms后"expect(spy.called).to.be.trueclock.restore() // 恢复时间,否则将影响到其他测试
})
浏览器相关API调用的测试
session的栗子
/*** WebStorage封装* @param storage 存储对象,支持localStorage和sessionStorage* @return 返回封装的存储接口*/
export function Storage(storage: Storage) {/*** 获取session* @param key * @return 返回JSON解析后的值*/function get(key: string) {const item = storage.getItem(key);if (!item) {return null;} else {return JSON.parse(item);}}
}
export default Storage(window.sessionStorage);/* ↑↑↑↑被测函数↑↑↑↑ */
/* ↓↓↓↓测试代码↓↓↓↓ */describe('session', () => {beforeEach('清空sessionStorage', () => {window.sessionStorage.clear();});describe('获取session#get', () => {it('获取简单类型值', () => {window.sessionStorage.setItem('foo', JSON.stringify('foo'))expect(session.get('foo')).to.equal('foo');});it('获取引用类型值', () => {window.sessionStorage.setItem('object', JSON.stringify({}))expect(session.get('object')).to.deep.equal({});});it('获取不存在的session', () => {expect(session.get('aaa')).to.be.null;});});
})
设置title的栗子
/*** 设置tab页title* @param title */
export function setTitle(title) {// 当页面中嵌入了flash,并且页面的地址中含有“片段标识”(即网址#之后的文字)IE标签页标题被自动修改为网址片段标识if (userAgent().app === Browser.MSIE || userAgent().app === Browser.Edge) {setTimeout(function () {document.title = title}, 1000)} else {document.title = title}
}/* ↑↑↑↑被测函数↑↑↑↑ */
/* ↓↓↓↓测试代码↓↓↓↓ */it('设置tab页title#setTitle', () => {// 这个测试不是个好的测试,测试代码更改了页面DOM结构const originalTitle = document.title; // 保存原始title,测试结束后恢复const clock = sinon.useFakeTimers()setTitle('test title');clock.tick(1000)expect(document.title).to.equal('test title')setTitle(originalTitle); // 恢复原本的titleclock.tick(1000)clock.restore()
})
React 组件单元测试
React组件的测试本质上和其他单元测试区别不大,但是React 组件单元测试由于涉及到“浏览器渲染”问题,在复杂度上高很多,并且React组件除基础UI组件(如Button、Link)等底层组件外,其实很难保持其“规模”在“最小测试单元”这一范围内,虽然可以通过“浅渲染”(不渲染子组件)降低组件的复杂度。从单元测试的范畴和意义出发来看,其实大多数UI组件的测试很难归入单元测试中。由于“大家貌似都觉得理所应当”,本文档依然将UI组件测试作为单元测试介绍。
没有打开盒子时,我们无法决定猫是否还活着。
React并非一个黑盒,因此想要测试React,必须先了解React是如何渲染的。当然,咱们不需要去了解React的源码,知道个大概就好了。
简单来讲,一个React组件渲染到页面中需要下面几步。
-
根据状态计算出虚拟DOM对象
-
根据虚拟DOM生成真实的Dom结构
-
挂载Dom到页面中
但是在测试中,我们不需要挂载组件到页面中,只需要有一个Dom环境即可,如果你不需要完整的渲染组件甚至可以没有Dom环境。
Enzyme
Enzyme(酶,催化剂)是Airbnb公司提供的React 测试工具,提供了Shallow Rendering(浅渲染) | Full Rendering (全渲染)| Static Rendering (静态渲染)几种方式来渲染方式来测试React组件,一般常用浅渲染的方式进行测试。
使用Enzyme进行测试前需要先初始化React适配器,具体配置请查看官方手册
Shallow Rendering
只渲染组件最外层结构,不会渲染子组件
一个完整的Shallow渲染测试的实例:
import * as React from 'react'
import * as classnames from 'classnames'
import { ClassName } from '../helper'
import * as styles from './styles.desktop.css'const AppBar: React.StatelessComponent<any> = function AppBar({ className, children, ...props } = {}) {return (<div className={classnames(styles['app-bar'], ClassName.BorderTopColor, className)} {...props}>{children}</div>)
}export default AppBarimport * as React from 'react';
import { shallow } from 'enzyme';
import { expect } from 'chai';
import AppBar from '../ui.desktop'describe('ShareWebUI', () => {describe('AppBar@desktop', () => {describe('#render', () => {it('默认渲染', () => {shallow(<AppBar></AppBar>) });it('正确渲染子节点', () => {const wrapper = shallow(<AppBar><div>test</div></AppBar>)expect(wrapper.contains(<div>test</div>)).to.be.true});it('允许设置ClassName', () => {const wrapper = shallow(<AppBar className='test'></AppBar>)expect(wrapper.hasClass('test')).to.be.true});it('允许设置其他自定义props', () => {const wrapper = shallow(<AppBar name='name' age={123}></AppBar>)expect(wrapper.props()).to.be.include({ name: 'name', age: 123 })});});});
});
Full Rendering
渲染组件真实的Dom结构,如果需要测试原生事件,则必须使用这种渲染方式。
Static Rendering
类似于爬虫拿到的结果,拿到页面的HTML字符串,使用Cheerio进行操作。
Enzyme的浅渲染模式下的事件模拟并非是真的事件触发,实际上它是一种“障眼法”实现,例如buttonWrapper.simulate(‘click’)只是简单了调用传给Button组件的onClick参数的那个函数而已。
具体描述:airbnb.io/enzyme/docs…
Enzyme的很多坑:airbnb.io/enzyme/docs…
如果在异步操作中调用setState(),则测试时需要在下一个时钟周期中进行断言(类似的问题较多):
/* 模拟点击 /
wrapper.find(‘UIIcon’).simulate(‘click’)
expect(wrapper.state(‘loadStatus’)).to.equal(1) // 点击立即改变加载状态为正在加载
/ 在promise.then 中设置setState,因此需要在下一个时钟进行断言 */
setTimeout(() => {
expect(wrapper.state(‘loadStatus’)).to.equal(2)
done()
}, 0)
集成测试(Integration Testing)
集成测试是指在单元测试的基础上,将已测试过的单元测试函数进行组合集成暴露出的高层函数或类的封装,对这些函数或类进行的测试。
集成测试最大的难点就是颗粒度较大,逻辑更加复杂,外部因素更多,无法保证测试的可控和独立性。解决方式是使用测试桩(测试替身),即将调用的子函数或模块替换掉,即可以隐藏子模块的细节并且可以控制子模块的行为以达到预期的测试。(这里的前提是子模块已经经过完整的单元测试进行覆盖,因此可以假定为子模块状态可知。)
Typescript编译成Commonjs:
// 编译前
import * as B from 'B'
import {fn} from 'C'export function A() { B.fn.name()fn.name()
}export class A1 {}// 编译后
define(["require", "exports", "B", "C"], function (require, exports, B, C_1) {"use strict";Object.defineProperty(exports, "__esModule", { value: true });function A() {B.fn.name();C_1.fn.name();}exports.A = A;var A1 = /** @class */ (function () {function A1() {}return A1;}());exports.A1 = A1;
});
导出的所有函数和类都在exports对象下,由于在调用时其实是调用的exports.exportName,如果希望Stub则需要Stub exports下的属性即可,Sinon提供的stub功能允许我们修改一个对象下的任意属性,注意必须是在一个对象下,也就是说不能直接sinon.stub(fn),只能sinon.stub(obj,‘fnName’)。而在ES6中可以通过import * as moduleName from ‘moduleName ’ 将整个模块导出到单个对象上,就可以解决Stub的问题。
考虑有下面的模块依赖:
其中模块A依赖模块B和C,模块B又依赖模块C,这种情况下我们可以选择只Stub模块C,这时候在模块B中的C模块也同样会被Stub影响,最好的方式是同时Stub模块B和模块C。
栗子
import { clientAPI } from '../../../clientapi/clientapi';/*** 通过相对路径获取缓存信息* @param param0 参数对象* @param param0.relPath 相对路径*/
export const getInfoByPath: Core.APIs.Client.Cache.GetInfoByPath = function ({ relPath }) {return clientAPI('cache', 'GetInfoByPath', { relPath });
}/* ↑↑↑↑被测函数↑↑↑↑ */
/* ↓↓↓↓测试代码↓↓↓↓ */import { expect } from 'chai'
import { stub } from 'sinon'
import * as clientapi from '../../../clientapi/clientapi';import { getInfoByPath, getUnsyncLog, getUnsyncLogNum } from './cache'describe('cache', () => {beforeEach('stub', () => {stub(clientapi, 'clientAPI')})afterEach('restore', () => {clientapi.clientAPI.restore()})it('通过相对路径获取缓存信息#getInfoByPath', () => {getInfoByPath({ relPath: 'relPath' })expect(clientapi.clientAPI.args[0][0]).to.equal('cache') // 请求资源正确expect(clientapi.clientAPI.args[0][1]).to.equal('GetInfoByPath') // 请求方法正确expect(clientapi.clientAPI.args[0][2]).to.deep.equal({ relPath: 'relPath' }) // 请求体正确})
})
或者使用Sinon提供的Sandbox,在restore时更加简单,不需要单独restore每一个被Stub的对象。
import { rsaEncrypt } from '../../util/rsa/rsa';
import { getNew} from '../apis/eachttp/auth1/auth1';
/*** 认证用户* @param account {string}* @param password {string}*/
export function auth(account: string, password: string, ostype: number, vcodeinfo?: Core.APIs.EACHTTP.Auth1.VcodeInfo): Promise<Core.APIs.EACHTTP.AuthInfo> {return getNew({account,password: encrypt(password),deviceinfo: {ostype: ostype},vcodeinfo});
}/* ↑↑↑↑被测函数↑↑↑↑ */
/* ↓↓↓↓测试代码↓↓↓↓ */
import { createSandbox } from 'sinon'
import * as auth1 from '../apis/eachttp/auth1/auth1'
import * as rsa from '../../util/rsa/rsa'
const sandbox = createSandbox()
describe('auth', () => {beforeEach('stub',()=>{sandbox.stub(rsa,'rsaEncrypt')sandbox.stub(auth1,'getNew')})afterEach('restore',()=>{sandbox.restore()})it('认证用户#auth', () => {auth('account', 'password', 1, { uuid: '12140661-e35b-4551-84cf-ce0e513d1596', vcode: '1abc', ismodif: false })rsa.rsaEncrypt.returns('123') // 控制返回值expect(rsa.rsaEncrypt.calledWith('password')).to.be.trueexpect(auth1.getNew.calledWith({account: 'account',password: '123',deviceinfo: {ostype: 1},vcodeinfo: {uuid: '12140661-e35b-4551-84cf-ce0e513d1596',vcode: '1abc',ismodif: false}})).to.be.true})
}
参考文档:
Stubs - Sinon.JS Stub的相关概念和使用Sandboxes - Sinon.JS Sandbox(沙盒)的相关概念和使用
端到端测试(E2E Testing)
端到端测试是最顶层的测试,即完全作为一个用户一样将程序作为一个完全的黑盒,打开应用程序模拟输入,检查功能以及界面是否正确。
端到端测试需要解决的一些问题:
环境问题
即如何保证每次执行测试前的环境是“干净的”,比如需要检查列表为空的表现,如果上一次测试新增了列表,则后一次测试将无法得到列表为空的状态。
最简单的解决方式是在所有测试执行前或测试执行后调用外部脚本清除数据库等,或者可以通过拦截请求并自定义响应的方式来解决(这样会导致测试复杂度变高,并且不够”真实“)。
元素查找
如果代码经常变动,组件结构经常变化,如果根据DOM结构来查找操作元素,那么你将陷入维护选择器的地狱中。最佳实践是使用test-id的方式,但是这种方式需要开发人员和测试人员配合,在可操作元素上定义语义化的test-id。
操作等待
诸如异步网络请求导致界面变化,或界面动画等,将使得获取操作元素的时机未知。解决方案持续等待直到监听的请求完成,期望的元素成功获取到。
使用操作而不是断言
应该更多的依赖操作,而不是依赖断言。例如如果某个操作依赖元素A存在,你不需要”判断元素A在页面中是否存在”,而应该去”直接获取元素A,并操作”,因为如果元素A不存在,那么肯定将获取不到,断言后的操作将没有意义,因此可以直接使用操作取代断言等待功能。
最后:【可能给予你帮助】
这些资料,对于考虑【软件测试】技能进阶的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴我走过了最艰难的路程,希望也能帮助到你
关注我的微信公众号【程序媛木子】免费获取
我的学习交流群:644956177群里有技术大牛一起交流分享~
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
相关文章
- git工程化 自动生成changeLog 发布版本
git log 生成 changeLog 在进行git仓库的自动化管理时,发布前往往需要CI服务器自动生成 CHANGELOG.MD ,本文介绍如何自动changeLog.md自动生成的思路。 文章目录git log 生成 changeLog生成流程关键问题使用 Git 自带的提交模板 Commit Template 来解决…...
2024/4/27 15:04:11 - 北京双眼皮修复 韩勋
...
2024/4/27 14:35:32 - 北京三院双眼皮多少钱
...
2024/4/26 23:23:27 - Vue学习从入门到精通(一)(转)
Vue学习从入门到精通(一) 2018年08月08日 18:54:32 JackLee18 阅读数:2173最近公司由于业务拓展,需要进行小程序相关的开发,本着朝全栈开发者努力,决定学习下Vue,去年csdn送了一本《Vue.js权威指…...
2024/4/21 16:22:12 - (三)weex 窥探 运行我的第一个HelloWorld 应用源码结构说明 打包运行到app上面
创建第一个Weex应用 cmd 到要创建的目录 就是。。cd 目录名称这一步我相信已经很基础了,,但是我仍然要操作一波给大家 cd H:\week H: weex create hello-week他会出现 ?Project name hello-week 这就很有趣,,我还以为卡了呢 …...
2024/4/27 4:55:39 - 使用开源微前端框架 Luigi 创建一个基于微前端架构的工程
官网地址 微前端通常被称为“前端微服务”。 它们允许您将大型单体前端分解为独立的、可扩展的、可以协同工作的独立部分。 微前端架构对于复杂的产品或拥有众多团队的公司尤其有用,可以帮助您创建一致的 Web 应用程序。 观看此视频,其中解释了微前端架…...
2024/4/19 14:36:20 - 使用 Flask 和 AngularJS 构建博客 - 2
注:该文作者是 John Kevin M. Basco,原文地址是 Building a blog using Flask and AngularJS Part 2 注:翻译的第一部分请移步到 - 使用 Flask 和 AngularJS 构建博客 - 1 这是这个教程系列的第二部分,如果你还没有都第一部分&…...
2024/4/27 13:44:02 - 反思记 教育终极目的
反思记 教育终极目的 2012年01月17日 用400多万元人民币换取一本50万字的学习论文集,会是一笔划算的投资吗? 如果再考虑到论文写作者们被改变的职业理念和状态,以及他们对无数乡村孩子可能产生的积极影响,答案不言自明。 这95名乡村教师――湖北省“农村教师资助…...
2024/4/20 14:27:10 - 北京那家做北京那家做双眼皮好
...
2024/4/20 13:02:58 - 北京那家做双眼皮的好
...
2024/4/27 14:52:58 - 北京华韩北京华韩双眼皮怎么样
...
2024/4/27 13:27:59 - 北京 双眼皮哪家医院比较好
...
2024/4/27 16:50:31 - 北京 双眼皮哪家医院
...
2024/4/27 16:18:03 - NodeJS作为Web架构中间层的使用
截至2016年12月,中国网民规模已达7.31亿。传统的网站系统是否能够支撑得起如此庞大的且不断增长的用户访问并且为用户提供体验友好的页面? 一、传统的前后端: 二、传统的前后端分离问题: 性能问题: 1、渲染、数据都在…...
2024/4/21 16:22:05 - 模态框的引入
前言: 在显示竞价结果时,引入了模态框,模态框在使用之前要先引入bootstrap.css(bootstrap.jsbootstrap.min.js,两种js文件的区别是一个未压缩一个压缩,无需全部引入)。所有插件都依赖jQuery&…...
2024/4/21 16:22:04 - vue.js v-cloak指令
可以使用 v-cloak 指令设置样式,这些样式会在 Vue 实例编译结束时,从绑定的 HTML 元素上被移除。 当网络较慢,网页还在加载 Vue.js ,而导致 Vue 来不及渲染,这时页面就会显示出 Vue 源代码。我们可以使用 v-cloak 指令…...
2024/4/27 17:02:01 - 半月型帮杨颖做双眼皮医生
...
2024/4/21 16:22:03 - 半扇形双眼皮图片
...
2024/4/27 5:57:30 - 半扇形半扇形双眼皮明星
...
2024/4/21 16:22:00 - 手把手教你使用Js实现前后台传送Json
无论使用什么框架都存在着从controller向Html页面或者jsp页面传递数据的问题,最常用的方式是传递Json字符串。以前对这块知识有些模糊,现在整理一下。 【Jquery基本方法】 实现传值常用的是Jquery以及内部封装的ajax。首先看一下jquery的get(…...
2024/4/21 16:21:59
最新文章
- NXP恩智浦 S32G电源管理芯片 VR5510 安全概念 Safety Concept (万字长文详解,配21张彩图)
NXP恩智浦 S32G电源管理芯片 VR5510 安全概念 Safety Concept (万字长文详解,配21张彩图) 1. 简介 本应用笔记描述了与S32G处理器和VR5510 PMIC相关的安全概念。该文档涵盖了S32G和VR5510的安全功能以及它们如何相互作用,以确保对ASIL D安全完整性级别…...
2024/4/27 17:50:06 - 梯度消失和梯度爆炸的一些处理方法
在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言,在此感激不尽。 权重和梯度的更新公式如下: w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...
2024/3/20 10:50:27 - MQ的作用及分类
概念: MQ(message queue),从字面意思上看,本质是个队列,FIFO先入先出,只不过队列中存放的内容是message而已,还是一种跨进程的通信机制,用于上下游传递消息。在互联网架构中,MQ是一…...
2024/4/27 11:43:39 - OpenAI 宣布, ChatGPT 网页端无需注册就能立即使用(2024年4月1日)
今天,OpenAI宣布,为了让更多人轻松体验人工智能的强大功能,现在无需注册账户即可立即使用 ChatGPT。这一变化是他们使命的核心部分,即让像 ChatGPT 这样的工具广泛可用,让世界各地的人们都能享受到 AI 带来的好处。 网…...
2024/4/23 17:22:07 - 【外汇早评】美通胀数据走低,美元调整
原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...
2024/4/26 18:09:39 - 【原油贵金属周评】原油多头拥挤,价格调整
原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...
2024/4/26 20:12:18 - 【外汇周评】靓丽非农不及疲软通胀影响
原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...
2024/4/26 23:05:52 - 【原油贵金属早评】库存继续增加,油价收跌
原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...
2024/4/27 4:00:35 - 【外汇早评】日本央行会议纪要不改日元强势
原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...
2024/4/25 18:39:22 - 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响
原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...
2024/4/27 14:22:49 - 【外汇早评】美欲与伊朗重谈协议
原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...
2024/4/26 21:56:58 - 【原油贵金属早评】波动率飙升,市场情绪动荡
原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...
2024/4/27 9:01:45 - 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试
原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...
2024/4/26 16:00:35 - 【原油贵金属早评】市场情绪继续恶化,黄金上破
原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...
2024/4/25 18:39:16 - 【外汇早评】美伊僵持,风险情绪继续升温
原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...
2024/4/25 18:39:16 - 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势
原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...
2024/4/26 19:03:37 - 氧生福地 玩美北湖(上)——为时光守候两千年
原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...
2024/4/26 22:01:59 - 氧生福地 玩美北湖(中)——永春梯田里的美与鲜
原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...
2024/4/25 18:39:14 - 氧生福地 玩美北湖(下)——奔跑吧骚年!
原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...
2024/4/26 23:04:58 - 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!
原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...
2024/4/25 2:10:52 - 「发现」铁皮石斛仙草之神奇功效用于医用面膜
原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...
2024/4/25 18:39:00 - 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者
原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...
2024/4/26 19:46:12 - 广州械字号面膜生产厂家OEM/ODM4项须知!
原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...
2024/4/27 11:43:08 - 械字号医用眼膜缓解用眼过度到底有无作用?
原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...
2024/4/27 8:32:30 - 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...
解析如下:1、长按电脑电源键直至关机,然后再按一次电源健重启电脑,按F8健进入安全模式2、安全模式下进入Windows系统桌面后,按住“winR”打开运行窗口,输入“services.msc”打开服务设置3、在服务界面,选中…...
2022/11/19 21:17:18 - 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。
%读入6幅图像(每一幅图像的大小是564*564) f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...
2022/11/19 21:17:16 - 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...
win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面,在等待界面中我们需要等待操作结束才能关机,虽然这比较麻烦,但是对系统进行配置和升级…...
2022/11/19 21:17:15 - 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...
有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows,请勿关闭计算机”的提示,要过很久才能进入系统,有的用户甚至几个小时也无法进入,下面就教大家这个问题的解决方法。第一种方法:我们首先在左下角的“开始…...
2022/11/19 21:17:14 - win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...
置信有很多用户都跟小编一样遇到过这样的问题,电脑时发现开机屏幕显现“正在配置Windows Update,请勿关机”(如下图所示),而且还需求等大约5分钟才干进入系统。这是怎样回事呢?一切都是正常操作的,为什么开时机呈现“正…...
2022/11/19 21:17:13 - 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...
Win7系统开机启动时总是出现“配置Windows请勿关机”的提示,没过几秒后电脑自动重启,每次开机都这样无法进入系统,此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一:开机按下F8,在出现的Windows高级启动选…...
2022/11/19 21:17:12 - 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...
有不少windows10系统用户反映说碰到这样一个情况,就是电脑提示正在准备windows请勿关闭计算机,碰到这样的问题该怎么解决呢,现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法:1、2、依次…...
2022/11/19 21:17:11 - 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...
今天和大家分享一下win7系统重装了Win7旗舰版系统后,每次关机的时候桌面上都会显示一个“配置Windows Update的界面,提示请勿关闭计算机”,每次停留好几分钟才能正常关机,导致什么情况引起的呢?出现配置Windows Update…...
2022/11/19 21:17:10 - 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...
只能是等着,别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚,只能是考虑备份数据后重装系统了。解决来方案一:管理员运行cmd:net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...
2022/11/19 21:17:09 - 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?
原标题:电脑提示“配置Windows Update请勿关闭计算机”怎么办?win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢?一般的方…...
2022/11/19 21:17:08 - 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...
关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!关机提示 windows7 正在配…...
2022/11/19 21:17:05 - 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...
钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...
2022/11/19 21:17:05 - 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...
前几天班里有位学生电脑(windows 7系统)出问题了,具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面,长时间没反应,无法进入系统。这个问题原来帮其他同学也解决过,网上搜了不少资料&#x…...
2022/11/19 21:17:04 - 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...
本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法,并在最后教给你1种保护系统安全的好方法,一起来看看!电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中,添加了1个新功能在“磁…...
2022/11/19 21:17:03 - 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...
许多用户在长期不使用电脑的时候,开启电脑发现电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机。。.这要怎么办呢?下面小编就带着大家一起看看吧!如果能够正常进入系统,建议您暂时移…...
2022/11/19 21:17:02 - 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...
配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!配置windows update失败 还原更改 请勿关闭计算机&#x…...
2022/11/19 21:17:01 - 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...
不知道大家有没有遇到过这样的一个问题,就是我们的win7系统在关机的时候,总是喜欢显示“准备配置windows,请勿关机”这样的一个页面,没有什么大碍,但是如果一直等着的话就要两个小时甚至更久都关不了机,非常…...
2022/11/19 21:17:00 - 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...
当电脑出现正在准备配置windows请勿关闭计算机时,一般是您正对windows进行升级,但是这个要是长时间没有反应,我们不能再傻等下去了。可能是电脑出了别的问题了,来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...
2022/11/19 21:16:59 - 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...
我们使用电脑的过程中有时会遇到这种情况,当我们打开电脑之后,发现一直停留在一个界面:“配置Windows Update失败,还原更改请勿关闭计算机”,等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢࿰…...
2022/11/19 21:16:58 - 如何在iPhone上关闭“请勿打扰”
Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...
2022/11/19 21:16:57