记录使用vue-test-utils + jest 在uniapp中进行单元测试
目录
- 前情
- 安装依赖
- package.json配置
- jest配置
- 测试文件目录
- 编写setup.js
- 编写第一个测试文件
- jest.fn()和jest.spyOn()
- jest 解析scss失败
- 测试vuex
- $refs
- 定时器
- 测试函数调用n次
- 手动调用生命周期
- 处理其他模块导入的函数
- 测试插槽
前情
- uniapp推荐了测试方案
@dcloudio/uni-automator
,属于自动化测试,api提供的示例偏重于渲染组件,判断当前渲染的组件是否和预期一致 - vue推荐的测试方案
vue test utils
,属于单元测试,可以搭配jest、mocha等单测运行器
我选了方案2🕶️
关于vue的组件测试,vue官方提到:
你的 Vue 应用中大部分内容都应该由组件测试来覆盖,我们建议每个 Vue 组件都应有自己的组件测试文件。
当进行测试时,请记住,测试这个组件做了什么,而不是测试它是怎么做到的对于 视图 的测试:根据输入 prop 和插槽断言渲染输出是否正确。
对于 交互 的测试:断言渲染的更新是否正确或触发的事件是否正确地响应了用户输入事件
本身的测试写起来很简单,就是挺多东西需要配置的,比较麻烦,记录在后文
安装依赖
- @vue/test-utils
vue2项目安装:
npm install --save-dev @vue/test-utils@1
不指定的话默认安装最新,适合vue3项目吧 - jest
- vue-jest:为了处理.vue文件
npm install --save-dev @vue/vue2-jest@29
(最后写jest版本) - babel-jest
- jest-environment-jsdom
jest版本在27以上,是要安装jest-environment-jsdom
的
其他版本下如果报错:
[vue-test-utils]: window is undefined, vue-test-utils needs to be run in a browser environment. You can run the tests in node using jsdom
可以尝试:
npm install --save-dev jsdom jsdom-global
// 在测试的设置 / 入口中
require('jsdom-global')()
package.json配置
加一条就好
"scripts": {"test": "jest"
},
jest配置
可以配在package.json
的jest选项中
也可以新建jest.config.js
,我选了后者
module.exports = {moduleFileExtensions: ['js','vue'],transform: {'^.+\\.vue$': '<rootDir>/node_modules/@vue/vue2-jest','^.+\\.js$': '<rootDir>/node_modules/babel-jest'},moduleNameMapper: { // webpack中设置了别名,@设置为/src 的别名,就需要配这个'^@/(.*)$': '<rootDir>/src/$1'},testMatch: ['**/__tests__/**/*.spec.js'],transformIgnorePatterns: ['<rootDir>/node_modules/'],testEnvironment: "jsdom" // jest v27以上要加
}
⚠️:
官网提到的一个注意点:
如果你使用了 Babel 7 或更高版本,
你需要在你的 devDependencies 里添加 babel-bridge
($ npm install --save-dev babel-core@^7.0.0-bridge.0)。
我在运行时有相关的报错提示,所以我也按照这样安装了
如果你也有的话,可以参考一下
测试文件目录
新建__tests__
目录,放在src目录下可以,根目录下也可以
(注意别少打s)
目录下的测试文件扩展名应该是.spec.js
或者test.js
,我选了前者
这个也可以改,想改去找jest文档————
编写setup.js
通常用于执行一些全局的设置或引入一些测试所需的全局依赖,以确保这些设置和依赖在所有测试文件中都可用!
jest.config.js
中新加一条:
setupFiles: ["./__tests__/setup.js"]
__test__
文件夹下新建setup.js
1.项目中用到的uni或者wx的api是识别不了的,所以放在这里预先配置一下
2.在Vue.prototype上挂载的比如$toast、$api,$store、直接用this.调用的时候也是识别不了的,也要在这配置一下
localVue可以理解成创建本地的vue实例,可以使用localVue.prototype
挂载一些东西而不会污染到真正的Vue.prototype
,我在这挂到全局了,实际上可以在每个单独的测试文件中都create新的
import { createLocalVue } from "@vue/test-utils";
import Vuex from 'vuex'
import axios from 'axios'const CODE = '用户登录凭证';
// 创建的一个 Vue 的本地拷贝
const localVue = createLocalVue()localVue.use(Vuex)
const store = new Vuex.Store({state: {},mutations: {login: jest.fn()}
})
localVue.prototype.$store = store
localVue.prototype.$toast = jest.fn()
// 后面很多场景的使用是const {confirm} = await this.$modal(xxx), 这里直接模拟cofirm为true
localVue.prototype.$modal = jest.fn(() => Promise.resolve({ confirm: true }))
localVue.prototype.$api = {student: {studentLogin: jest.spyOn(axios, 'post')},
}global.uni = {showLoading: jest.fn(),hideLoading: jest.fn(),navigateTo: jest.fn(),switchTab: jest.fn(),getStorageSync: jest.fn(),setStorageSync: jest.fn(),login: jest.fn(() => Promise.resolve([,CODE]))
}
global.setValue = (target, value) => {target.element.value = valuetarget.trigger('input')
}
global.wx = global.uni
global.localVue = localVue
ps:这里挂了一个全局的方法setValue
,因为官方的那个我使用会报错显示没有setValue(),查看setValue(),不知道是不是因为我的input是小程序的🤔
编写第一个测试文件
对组件StudentLogin.vue
,新建studentLogin.spec.js
变更一个响应式属性之后,为了断言这个变化,测试需要等待 Vue 完成更新,可以
- await vm.nextTick() 2. await 操作,比如trigger
import { shallowMount } from "@vue/test-utils";
import StudentLogin from '@/pages/student-login/student-login'const TEST_VALUE = '123456'const TEST_TIP = {NO_NUMBER: '请填写学号!',NO_PASSWORD: '请填写密码!'}
// describe(name, fn): 表示一组测试,如果没有describe,那整个测试文件就是一个describe。name是这组测试的名字,fn是这组测试要执行的函数。
describe('StudentLogin.vue', () => {let wrapper;beforeEach(() => {// shallowMount和mount区别在于不会挂载子组件,比较适合单元测试,子组件的测试逻辑单独写wrapper = shallowMount(StudentLogin, {localVue})})// formSubmit触发时,输入账号没输入密码,提示请填写密码!test('if formSubmit triggered with number but no password, show tip', async () => {setValue(wrapper.find('input[name="number"]'), TEST_VALUE)await wrapper.vm.$nextTick();await wrapper.find('.submit-btn').trigger('click')expect(localVue.prototype.$toast).toBeCalledWith('error', TEST_TIP.NO_PASSWORD)})// formSubmit调用后,应该发起请求it('if formSubmit done, send request', async () => {setValue(wrapper.find('input[name="number"]'), TEST_VALUE)setValue(wrapper.find('input[name="password"]'), TEST_VALUE)await wrapper.vm.formSubmit()expect(localVue.prototype.$api.student.studentLogin).toBeCalled();expect(localVue.prototype.$api.student.studentLogin).toBeCalledWith(TEST_VALUE, TEST_VALUE, CODE)})// 销毁所有被创建的 Wrapper 实例enableAutoDestroy(afterEach)
})
jest.fn()和jest.spyOn()
承接上文:
轻轻记录一下jest.fn()
和jest.spyOn()
他们都是用来模拟函数的行为,都会跟踪函数的调用和传参
区别:jest.fn()
是创建一个全新的模拟函数,jest.spyOn()
一般是模拟对象上的现有方法
比如
页面需要axios发请求,但是我们测试的时候不需要实际调用,
就可以利用
localVue.prototype.$api = {student: {studentLogin: jest.spyOn(axios, 'post')},
}
使用场景非常多,后文也会涉及
他们两返回的其实就是mockFn,在jest官网有非常多对mockFn的操作
指路:mockFn
我常用的一个mockFn.mockResolvedValue(value)
例如:
测试这个函数,是否成功发送请求,但我们无需发送真的请求,就可以模拟返回值
async getList() {const { data } = await this.$api.student.getData()this.list = data
}
// test.spec.js
test('', async () => {localVue.prototype.$api.student.getData.mockResolvedValue({list: [1,2,3]})await wrapper.vm.getList()expect(wrapper.list.length).toBe(3)
})
⚠️提醒一下自己,注意:
比如说我们要断言,trigger某个操作或者更新了页面之后,某个函数应该要被调用
会使用
const spy = jest.spyOn(wrapper.vm, 'someFunction')
expect(spy).toBeCalled()
但要注意这个必须要写在更新操作之前,如果写在之后是会断言错误的
👇 jest.spyOn
写在了trigger之后,也就是开始跟踪的时候已经触发完了,
那么expect(infoSpy).toBeCalled()
就会失败
test('if term picker triggered', async () => {const picker = wrapper.findComponent('picker')await picker.trigger("change", 1);const infoSpy = jest.spyOn(wrapper.vm, 'getInfo')expect(wrapper.vm.termIndex).toBe(1)expect(infoSpy).toBeCalled()})
jest 解析scss失败
比如这个页面有引入scss:
import { THEME_COLOR } from "@/uni.scss";
如果不做配置的话就会报错
解决方法:
新建一个styleMock.js
// styleMock.js
module.exports = {process() {return {code: `module.exports = {};`,};},
};
然后在jest.config.js
中配置transform
:
transform: {'^.+\\.vue$': '<rootDir>/node_modules/@vue/vue2-jest','^.+\\.js$': '<rootDir>/node_modules/babel-jest','\\.(css|less|scss|sass)$': '<rootDir>/styleMock.js',
},
然后运行npm run test
,如果还是没生效,可以试试关闭编辑器重新启动
测试vuex
这里不提官网有的部分,有需要可自查
在组件中测试vuex
目前场景是这个组件在计算属性中使用了mapState
computed: {... mapState(['flag', 'userInfo'])
}
然后当userInfo.level = 1 && flag = 1
时候要渲染某容器,我需要测试这个,那么就需要修改state
中的数据
由于前面在setup.js中已经在localVue上安装了vuex,这里就通过localVue
来访问
localVue.prototype.$store.state.flag = 1
localVue.prototype.$store.state.userInfo = { level: 1 }
不要用store.commit(),不生效
⚠️:更改完数据后,要等待页面更新,记得await nextTick()一下,否则断言会失败
$refs
类似于这样的代码:
close() { // 清除校验结果this.$refs.form.clearValidate();this.$emit('close');
},
this.$refs.form.clearValidate();
会报错,提示找不到clearValidate这个function
解决方法1: 模拟一个form塞在stubs里
// 这里要写的是组件的名字,不是ref设置的名字const UniForms = {render: jest.fn(),methods: {validate: () => {},clearValidate:() => {}}}
wrapper = shallowMount(ForgetPassword, {localVue,stubs: {UniForms}
})
(模板上<uni-forms ref="form"></uni-forms>
)
但我这个例子用这个方法不太行,会影响我别的测试(一些元素渲染失败,wrapper.find时会找不到)
先记录在这吧
解决方法2:
加一行👇
wrapper.vm.$refs.form.clearValidate = jest.fn()
如果有要返回的数据,可以在jest.fn()
中直接模拟
比如说:
我们需要拿到返回的password、email,简单的jest.fn()
无法满足需求
const { password, email } = await this.$refs.form.validate();
设定jest.fn()模拟的函数,返回成功值
wrapper.vm.$refs.form.validate = jest.fn(() => Promise.resolve({ password: 1, email: 1
}))
后续:
又有一处用到$refs:
mounted() { this.$refs.form.setRules(this.formRules);
}
这次是在mounted()里使用,方法2就用不了了,因为需要先mount(wrapper),才能拿到wrapper.vm,但这里又是要在mounted中执行的,假如我们使用wrapper.vm.$refs.form.setRules = jest.fn()
其实就已经晚了,mounted已经执行完了
这个时候就可以用方法1~
定时器
检验有关定时器的方法
setTime(number) {this.codeText = `倒计时${number}s`;if(!number) {this.codeText = '发送验证码';this.isSending = false;this.timer = null;return;} else {number--;}this.timer = setTimeout(() => {this.setTime(number);}, 1000);
},
使用jest.useFakeTimers()
指定全局使用假的定时器api
jest.advanceTimersByTime(1000)
模拟时间快进1s
jest.useFakeTimers()
const sendCodeBtn = wrapper.findComponent('.send-code')
test('if setTime triggered 60, change btn content and start countdown', async () => {const setTimeSpy = jest.spyOn(wrapper.vm, 'setTime')await wrapper.vm.setTime(60)expect(sendCodeBtn.text()).toBe('倒计时60s')// 过一秒jest.advanceTimersByTime(1000)expect(setTimeSpy).toBeCalledWith(60 - 1)})test('if setTime triggered 0, change btn content and close timer', async () => {await wrapper.vm.setTime(0)expect(sendCodeBtn.text()).toBe('发送验证码')// 过一秒jest.advanceTimersByTime(1000)expect(wrapper.vm.timer).toBe(null)})
测试函数调用n次
本来想测
1.titleInput或contentInput无内容时 => 提示’请输入必要内容’
2.titleInput和contentInput都有内容时 => 不显示提示
(错误写法👇)
test("", async () => {await form.trigger('submit')expect(localVue.prototype.$toast).toHaveBeenCalledWith('none', '请输入必要内容')setValue(titleInput, TEST_VALUE)await form.trigger('submit')expect(localVue.prototype.$toast).toHaveBeenCalledWith('none', '请输入必要内容')setValue(contentInput, TEST_VALUE)await form.trigger('submit')expect(localVue.prototype.$toast).not.toHaveBeenCalled()
});
但上面这种写法是错的,实际上localVue.prototype.$toast
的调用是累积的,不是相互隔离的,第三次expect(localVue.prototype.$toast)
的时候实际上已经被调用三次了,那么not.toHaveBeenCalled()
就不可能通过测试
这时候应该使用toHaveBeenNthCalledWidth()
,第一个参数写n,表示第n次
第三次的时候不应该被调用,就用toHaveBeenCalledTimes()
判断总调用次数
test("", async () => {await form.trigger('submit')expect(localVue.prototype.$toast).toHaveBeenNthCalledWith(1, 'none', '请输入必要内容')setValue(titleInput, TEST_VALUE)await form.trigger('submit')expect(localVue.prototype.$toast).toHaveBeenNthCalledWith(2, 'none', '请输入必要内容')setValue(contentInput, TEST_VALUE)await form.trigger('submit')expect(localVue.prototype.$toast).not.toHaveBeenCalledTimes(3);
});
手动调用生命周期
比如说
(onLoad是小程序里的生命周期)
onLoad({code}) {this.code = +code;// 每10min刷新一次if(!this.code) {this.getSignInCode();this.timer = setInterval(() => { this.getSignInCode() }, 600000);}
}
这里想测试code为0的时候是否调用了函数getSignInCode,且过了10min是否再次调用
我想手动调用onLoad(),onLoad并不在wrapper.vm
上,不能通过wrapper.vm.onLoad
访问
可以通过两种方式找到:(这个组件名叫ShowQRcode
)
ShowQRcode.onLoad({ code: 1 })
wrapper.vm.$options.onLoad({ code: 1 })
但都会报错:this.getSignInCode is not a function
,因为getSignInCode是在wrapper.vm
上的,所以这里要更改this指向
ShowQRcode.onLoad.call(wrapper.vm, {code: 0 })
test('', () => {const signInSpy = jest.spyOn(wrapper.vm, 'getSignInCode')ShowQRcode.onLoad.call(wrapper.vm, { code: 0 })expect(signInSpy).toHaveBeenCalledTimes(1)jest.advanceTimersByTime(600000)expect(signInSpy).toHaveBeenCalledTimes(2)})
处理其他模块导入的函数
场景:
import { uploadImg } from '@/util/uploadImg.js';async selectImg(res) {// 上传图片const { url } = await uploadImg(res.tempFilePaths, 'files')this.imgPaths.push(url[0]);
}
如果要测试selectImg()
,当执行到uploadImg()
就会报错
我们就可以利用jest.mock
来模拟这个模块
记录一下jest.mock的简单使用:
官网的例子:
// banana.js
export default () => 'banana';
// test.spec.js
// 后续的测试中,任何导入./banana模块的代码将会被自动模拟,而不是实际的banana.js模块
jest.mock('./banana');
// 这个导入的bannana就被自动模拟了
const banana = require('./banana');
// 不会返回banana,因为被模拟了,默认返回undefined
banana(); // will return 'undefined'
还可以接收一个函数,显式指定模块导出的内容
// 相当于
// const mockFn = jest.fn(() => 'bannana'
// export default mockFn
jest.mock('./bannana', () => {return jest.fn(() => 'bannana');
});const bannana = require('./bannana');
bannana(); // Will return 'bannana';
所以这里就这样写:
// 相当于
// export const uploadImg = jest.fn(() => Promse.resolve({ data: TEST_UPLOAD_RESPONSE}))
jest.mock('@/util/uploadImg.js', () => ({otherFunction: xxx,uploadImg: jest.fn(() => Promise.resolve({ data: TEST_UPLOAD_RESPONSE }))
}));
测试插槽
项目比较简单,用插槽的地方很少,甚至没用到作用域插槽
这里只记录最简单的方法
官网是有例子的:测试插槽
就是在shallowMount的时候配置slots
beforeEach(() => {wrapper = shallowMount(Detail, {localVue,slots: {list: '<view>list</view>',operation: '<view>operation</view>'}})})
这里slots配置的就是模拟传入插槽的内容
比如list: '<view>list</view>'
,就是该组件内有一个插槽出口<slot name="list"></slot>
然后我们模拟传入这个插槽的内容是<view>list</view>
之后打印wrapper.html()
会发现插槽出口确实都被替换成了我们预设的内容
只需要断言expect(wrapper.html()).toContain('<view>list</view>')
即可完成测试
这里还出现一个问题,我有一个插槽出口长这样👇
<slot name="top"><view class="top__button" v-if="flag === 'xxx'"><text>{{ xxx }}</text></view>
</slot>
在插槽中指定了默认内容,且默认内容要通过v-if控制显示隐藏
并且这个地方我也写了一个测试,是测试top__button的显隐
如果我一开始预设的时候,预设了插槽top的内容,就会导致这个测试失败,因为找不到top__button了,直接被替换成了我预设的内容
其实失败的原因是我两个测试共用了一个wrapper的配置(习惯写在beforeEach里)
解决的方法就是在这个测试中,单独的再重新创建一个wrapper,不要预设slots就好
补充:
测试作用域插槽
参考:
https://juejin.cn/post/7119314584371986468?searchId=2023092122585499D5137C15C4283D9452
https://blog.csdn.net/pk142536/article/details/122255192
https://zhuanlan.zhihu.com/p/457648810
相关文章:
记录使用vue-test-utils + jest 在uniapp中进行单元测试
目录 前情安装依赖package.json配置jest配置测试文件目录编写setup.js编写第一个测试文件jest.fn()和jest.spyOn()jest 解析scss失败测试vuex$refs定时器测试函数调用n次手动调用生命周期处理其他模块导入的函数测试插槽 前情 uniapp推荐了测试方案dcloudio/uni-automator&…...
《C和指针》笔记30:函数声明数组参数、数组初始化方式和字符数组的初始化
文章目录 1. 函数声明数组参数2. 数组初始化方式2.1 静态初始化2.2 自动变量初始化 2.2 字符数组的初始化 1. 函数声明数组参数 下面两个函数原型是一样的: int strlen( char *string ); int strlen( char string[] );可以使用任何一种声明,但哪个“更…...
VBA技术资料MF64:遍历单元格搜索字符并高亮显示
【分享成果,随喜正能量】不要在乎他人的评论,不必理论与他人有关的是非,你只要做好自己就够了。苔花如米小,也学牡丹开。无论什么时候,都要有忠于自己的勇气,去做喜欢的事,去认识喜欢的人&#…...
一键智能视频编辑与视频修复算法——ProPainter源码解析与部署
前言 视频编辑和修复确实是随着电子产品的普及变得越来越重要的技能。有许多视频编辑工具可以帮助人们轻松完成这些任务如:Adobe Premiere Pro,Final Cut Pro X,Davinci Resolve,HitFilm Express,它们都提供一些视频修…...
Flutter开发环境的配置
2023-10最新版本 flutter SDK版本下载地址 https://flutter.cn/docs/development/tools/sdk/releases gradle各版本快速下载地址 https://blog.csdn.net/ii950606/article/details/109105402 JAVA SDK下载地址 https://www.oracle.com/java/technologies/downloads/#java…...
【超详细】Wireshark教程----Wireshark 分析ICMP报文数据试验
一,试验环境搭建 1-1 试验环境示例图 1-2 环境准备 两台kali主机(虚拟机) kali2022 192.168.220.129/24 kali2022 192.168.220.3/27 1-2-1 网关配置: 编辑-------- 虚拟网路编辑器 更改设置进来以后 ,先选择N…...
Linux命令(92)之rm
linux命令之rm 1.rm介绍 linux命令rm是用来删除一个或多个文件/目录,由于其删除的不可逆性,建议在日常工作中一定要慎用 2.rm用法 rm [参数] 文件/目录 rm常用参数 参数说明-r递归删除文件或目录-f不提示强制删除-i删除文件或目录前进行确认-v详细显…...
Mysql主从复制数据架构全面解读
大家好,我是山子,今天给大家分析Mysql 实现主从复制的方方面面,主从复制当然也是我们做读写分离的前提,以下内容是从各网络平台摘录整理总结归纳在一起的;内容已经从主从复制的各方面的维度进行了阐述;非常…...
ios证书类型及其作用说明
ios证书类型及其作用说明 很多刚开始接触iOS证书的开发者可能不是很了解iOS证书的类型功能和概念。下面对iOS证书的几个方面进行介绍。 apple开发账号分类: 免费账号: 无需支付费用给apple,使用个人信息注册的账号 可以开发测试安装&…...
警告-Ubuntu提示W: Possible missing firmware xxx解决方法
目录 现象原因解决方法 现象 当执行 sudo apt-get update或者sudo apt-get dist-upgrade时,有如下警告: W: Possible missing firmware /lib/firmware/rtl_nic/rtl8125a-3.fw for module r8169 W: Possible missing firmware /lib/firmware/rtl_nic/rt…...
有时候,使用 clang -g test.c 编译出可执行文件后,发现 gdb a.out 进行调试无法读取符号信息,为什么?
经过测试,gdb 并不是和所有版本的 llvm/clang 都兼容的 当 gdb 版本为 9.2 时,能支持 9.0.1-12 版本的 clang,但无法支持 16.0.6 版本的 clang 可以尝试使用 LLVM 专用的调试器 lldb 我尝试使用了 16.0.6 版本的 lldb 调试 16.0.6 的 clan…...
UG\NX二次开发 信息窗口的一些操作 NXOpen/ListingWindow
文章作者:里海 来源网站:王牌飞行员_里海_里海NX二次开发3000例,里海BlockUI专栏,C\C++-CSDN博客 简介: UG\NX二次开发 信息窗口的一些操作 NXOpen/ListingWindow 效果: 代码: #include "me.hpp" #include <NXOpen/ListingWindow.hxx> #include <…...
macbook电脑磁盘满了怎么删东西?
macbook是苹果公司的一款高性能笔记本电脑,受到很多用户的喜爱。但是,如果macbook的磁盘空间不足,可能会导致一些问题,比如无法开机、运行缓慢、应用崩溃等。那么,macbook磁盘满了无法开机怎么办,macbook磁…...
解释 RESTful API,以及如何使用它构建 web 应用程序
RESTful API是一种基于HTTP协议,使用REST架构风格设计的API。其核心思想是将所有的Web应用程序资源抽象为一组资源集合,并通过HTTP协议中的GET、POST、PUT、DELETE等几个方法对这些资源进行操作,使得Web应用程序能够方便地、高效地进行管理和…...
qml使用c++自定义listmodel数据
qml要使用c中自定义的model,首先该model类需要继承QAbstractListModel类,然后需要重写其中的三个函数,分别是 int rowCount(const QModelIndex &parent); QVariant data(const QModelIndex &index, int role Qt::DisplayRole); QHas…...
cf 解题报告 01
E. Power of Points Problem - 1857E - Codeforces 题意: 给你 n n n 个点,其整数坐标为 x 1 , … x n x_1,\dots x_n x1,…xn,它们位于一条数线上。 对于某个整数 s s s,我们构建线段[ s , x 1 s,x_1 s,x1], [ s , x…...
傅里叶系列 P1 的定价选项
如果您想了解更多信息,请查看第 2 部分和第 3 部分。 一、说明 这是第一篇文章,我将帮助您获得如何使用这个新的强大工具来解决金融中的半分析问题并取代您的蒙特卡洛方法的直觉。 我们都知道并喜欢蒙特卡洛数字积分方法,但是如果我告诉你你可…...
第二十届北京消防展即将开启,汉威科技即将精彩亮相
10月10日~13日,第二十届中国国际消防设备技术交流展览会,将在北京市顺义区中国国际展览中心新馆隆重举行。该展会由中国消防协会举办,是世界三大消防品牌展会之一,本届主题为“助力产业发展,服务消防救援”。届时将有4…...
mongodb、mysql、redis 区别
MongoDB、MySQL 和 Redis 是三种不同的数据库管理系统,它们在数据存储、访问模型和使用场景方面有一些显著的区别。 1. 数据存储模型: MongoDB:MongoDB 是一种文档数据库,它使用 BSON(Binary JSON)格式来存储数据。数据以文档的形式组织,每个文档可以有不同的字段,文档…...
【Flutter】Flutter Web 开发 如何从 URL 中获取参数值
【Flutter】Flutter Web 开发 如何从 URL 中获取参数值 文章目录 一、前言二、Flutter Web 中的 URL 处理三、如何从 URL 中获取参数四、实际业务中的用法五、完整示例六、总结 一、前言 大家好!我是小雨青年,今天我想和大家分享一下在 Flutter Web 开发…...
【Java 进阶篇】JDBC Statement:执行 SQL 语句的重要接口
在Java应用程序中,与数据库进行交互是一项常见的任务。为了执行数据库操作,我们需要使用JDBC(Java Database Connectivity)来建立与数据库的连接并执行SQL语句。Statement接口是JDBC中的一个重要接口,它用于执行SQL语句…...
Python与数据分析--Pandas操作进阶
目录 1.文件读取方式 1.1.绝对路径读取文件 1.2.相对路径读取文件 2.列表数据操作 2.1.列索引指定 2.2.代码数据对齐 3.创建新CSV文件 4.缺失值处理 4.1.缺失值创建 4.2.缺失值检索 4.3.缺失值查询 4.3.1.isnull()函数判断 4.3.2.notnull()函数判断 4.3.3.any()函数…...
国庆小练习
一、二、三 一、 创建一个双向链表, 将26个英文字母通过头插的方式插入到链表中 通过尾删的方式将数据读取出来并删除。main.c #include <my_head.h> #include "dblink.h"int main(int argc, const char *argv[]) {dblink *h create_head();for…...
springboot单体项目如何拆分成微服务
要将一个Spring Boot单体项目拆分成微服务,可以按照以下步骤进行操作: 识别业务域:首先,需要对单体项目进行业务域的划分。将项目中的功能按照业务领域进行分类,每个业务领域可以成为一个独立的微服务。 定义服务接口…...
解决recovery页面反转的问题
1.前言 在android 10.0的系统rom定制化开发工作中,在系统中recoverv的页面也是相关重要的一部分,在系统recovery ta升级等功能,都是需要recoverv功能的,在某些产品定制化中 在recovery的时候,发现居然旋转了180度&…...
如何使用nuScenes数据集格式的单帧数据推理(以DETR3D为例)
【请尊重原创!转载和引用文章内容务必注明出处!未经许可上传到某文库或其他收费阅读/下载网站赚钱的必追究责任!】 无论是mmdetection3D还是OpenPCDet都只有使用数据集(使用哪个数据集由配置文件里指定)训练和测试的代码,没有使用…...
大语言模型之十三 LLama2中文推理
在《大语言模型之十二 SentencePiece扩充LLama2中文词汇》一文中已经扩充好了中文词汇表,接下来就是使用整理的中文语料对模型进行预训练了。这里先跳过预训练环节。先试用已经训练好的模型,看看如何推理。 合并模型 这一步骤会合并LoRA权重࿰…...
iOS AVAudioSession 详解
iOS AVAudioSession 详解 - 简书 默认没有options,category 7种即可满足条件 - (BOOL)setCategory:(AVAudioSessionCategory)category error:(NSError **)outError API_AVAILABLE(ios(3.0), watchos(2.0), tvos(9.0)) API_UNAVAILABLE(macos); 有optionsÿ…...
26-网络通信
网络通信 什么是网络编程? 可以让设备中的程序与网络上其他设备中的程序进行数据交互(实现网络通信的)。 java.net.包下提供了网络编程的解决方案! 基本的通信架构有2种形式:CS架构( Client客户端/Server服…...
嵌入式Linux应用开发-基础知识-第十九章驱动程序基石③
嵌入式Linux应用开发-基础知识-第十九章驱动程序基石③ 第十九章 驱动程序基石③19.5 定时器19.5.1 内核函数19.5.2 定时器时间单位19.5.3 使用定时器处理按键抖动19.5.4 现场编程、上机19.5.5 深入研究:定时器的内部机制19.5.6 深入研究:找到系统滴答 1…...
怎么做网站引流/电脑优化大师官方免费下载
这是之前遇到的一道面试题,后来也确实在工作中实际遇到了。于是记录一下,如何(优雅的)比较两个未知结构的json。 假设,现在有两个简单的json文件。 {"id":1,"name":"testjson01","…...
成品网站qq客服/百度口碑官网
[client]#password your_passwordport 3306socket /tmp/mysql.sock[mysqld]port 3306socket /tmp/mysql.sockuser mariadbbasedir 这里是你自己的路径(查安装后的默认配置文件)datadir 这里是你自己的路径(查安装后的默认配置文件)log_er…...
榆林医疗网站建设/app推广平台网站
IPv6大概可以分为四类 1)单播地址(unicast address) 2)组播地址(Multicast Address) IPv6 IPv4 说明 ~ FF01::1 224.0.0.1 所有-节点地址 ~ FF01::2 224.0.0.2 所有-路由器地址 ~ FF02::1 …...
网站维护 代码/今日国际新闻最新消息大事
springboot使用lombok省略set和get方法使用lombok省略setget方法更多文章欢迎访问个人博客 www.herobin.top 使用lombok省略setget方法 首先在pom中加入lombok依赖包 pom.xml <dependency><groupId>org.projectlombok</groupId><artifactId>lombok…...
做网站服装app/网页设计大作业
转载至我的个人博客:LOVCHUN.COM我对编程很感兴趣,想当一名程序员。每个人可能会有很多兴趣:音乐、美食、游戏、编程等等,这些兴趣都可以指向某个行业,为什么最后你要选“编程”这个兴趣?编程并没有想象中的…...
南昌做网站多少钱/西安网站建设推广优化
1. 编写应用程序,创建类的对象,分别设置圆的半径、圆柱体的高,计算并分别显示圆半径、圆面积、圆周长,圆柱体的体积. 实现思路及关键代码:1) 编写一个圆类Circle,该类拥有:a) 一个成员变量,radius(私有,浮点型);//存放圆的半径b) 两个构造方法(无参、有参…...