记录使用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: {// 这里如果只是为了代码里不卡住,直接jest.fn()就可以login: jest.fn((state, userInfo) => state.userInfo = userInfo),setFlag: jest.fn((state,param) => state.flag = param.flag),logout: jest.fn((state) => state.userInfo = {})}
})
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 完成更新,可以

  1. 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()的话,就要在setup.js中localVue.prototype.$store上用jest.fn()具体实现mutation中的方法了,只是简单的用jest.fn()模拟是不生效的
像这样👇

const store = new Vuex.Store({state: {},mutations: {// 这里如果只是为了代码里不卡住,直接jest.fn()就可以login: jest.fn((state, userInfo) => state.userInfo = userInfo),setFlag: jest.fn((state,param) => state.flag = param.flag),logout: jest.fn((state) => state.userInfo = {})}
})

⚠️:更改完数据后,要等待页面更新,记得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

  1. ShowQRcode.onLoad({ code: 1 })
  2. 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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/96585.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue+vite项目静态文件引用丢失

排查问题&#xff1a; 查看vite.config.js中是否配置 base: ./, 如果配置了&#xff0c;那么 本地运行 例如&#xff1a;css中引入字体&#xff0c;可将字体文件直接放在public文件夹下通过"/"引入 如果需要动态引入图片&#xff0c;可通过 import defaultA fr…

(三)行为模式:8、状态模式(State Pattern)(C++示例)

目录 1、状态模式&#xff08;State Pattern&#xff09;含义 2、状态模式的UML图学习 3、状态模式的应用场景 4、状态模式的优缺点 &#xff08;1&#xff09;优点 &#xff08;2&#xff09;缺点 5、C实现状态模式的实例 1、状态模式&#xff08;State Pattern&#x…

光伏发电预测(LSTM、CNN_LSTM和XGBoost回归模型,Python代码)

运行效果&#xff1a;光伏发电预测&#xff08;LSTM、CNN_LSTM和XGBoost回归模型&#xff0c;Python代码&#xff09;_哔哩哔哩_bilibili 运行环境库的版本 光伏太阳能电池通过互连形成光伏模块&#xff0c;以捕捉太阳光并将太阳能转化为电能。因此&#xff0c;当光伏模块暴露…

【面试高高手】—— Java垃圾回收

文章目录 1.什么是Java垃圾回收&#xff08;Garbage Collection&#xff09;&#xff1f;为什么需要它&#xff1f;2. Java中的垃圾回收是如何工作的&#xff1f;请简要解释垃圾回收的工作原理。3.什么是垃圾对象&#xff08;Garbage Objects&#xff09;&#xff1f;如何确定一…

深入探究 C++ 编程中的资源泄漏问题

目录 1、GDI对象泄漏 1.1、何为GDI资源泄漏&#xff1f; 1.2、使用GDIView工具排查GDI对象泄漏 1.3、有时可能需要结合其他方法去排查 1.4、如何保证没有GDI对象泄漏&#xff1f; 2、进程句柄泄漏 2.1、何为进程句柄泄漏&#xff1f; 2.2、创建线程时的线程句柄泄漏 …

成功解决@Async注解不生效的问题,异步任务处理问题

首先&#xff0c;有这样一个异步监听方法 然后配置好了异步线程池 package com.fdw.study.config;import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Conf…

国产1.8V低电压输入,可用于驱动步进电机;H 桥驱动电路单元可以直接驱动IR-CUT

D6212是专为安防摄像头系统设计的驱动电路&#xff0c;电路由八路达林顿管 阵列和 H 桥驱动电路两个单元组成。八路达林顿管阵列均带有续流二极 管&#xff0c;可用于驱动步进电机&#xff1b;H 桥驱动电路单元可以直接驱动IR-CUT。单个 达林顿管在输入电压低至 1.8V 状态下支持…

口袋参谋:如何提升宝贝流量?这三种方法超实用!

​你的店铺能不能出爆款&#xff1f;提升单品流量是关键。 对于新手卖家来说&#xff0c;是缺乏运营技巧和运营经验的&#xff0c;运营技巧主要体现在标题写作、各种图片和视频制作等。 由于新手买家没有经验&#xff0c;习惯于直接使用数据包上传&#xff0c;导致宝贝没有展…

Java卷上天,可以转行干什么?

小刚是某名企里的一位有5年经验的高级Java开发工程师&#xff0c;每天沉重的的工作让他疲惫不堪&#xff0c;让他萌生出想换工作的心理&#xff0c;但是转行其他工作他又不清楚该找什么样的工作 因为JAVA 这几年的更新实在是太太太……快了&#xff0c;JAVA 8 都还没用多久&am…

Head-First-Design-Patterns

目录 1.策略模式 2.观察者模式 3.装饰者模式 4.工厂模式 5.单件模式 6.命令模式 7.适配器和外观模式 8.模板方法模式 9.迭代器和组合模式 10.状态模式 11.代理模式 12.复合模式 13.更好与设计模式相处 代码地址&#xff1a;https://github.com/wangzs2018/Design-…

cpp primer plus笔记01-注意事项

cpp尽量以int main()写函数头而不是以main()或者int main(void)或者void main()写。 cpp尽量上图用第4行的注释而不是用第5行注释。 尽量不要引用命名空间比如:using namespace std; 函数体内引用的命名空间会随着函数生命周期结束而失效&#xff0c;放置在全局引用的命名空…

22. 括号生成

22. 括号生成 回溯 class Solution {List<String> res new ArrayList<>();StringBuilder path new StringBuilder(); // path推荐这种写法public List<String> generateParenthesis(int n) {backtrack(n, 0, 0);return res;}void backtrack(int n, int …

PX4仿真添加world模型文件,并使用yolov8进行跟踪

前言 目的:我们是为了在无人机仿真中使用一个汽车模型,然后让仿真的无人机能够识别到这个汽车模型。所以我们需要在无人机仿真的环境中添加汽车模型。 无人机仿真中我们默认使用的empty.world文件,所以只需要将我们需要的模型添加到一起写进这个empty.world文件中去就可以…

通过套接字手动写一个回显服务器吧

背景:程序员主要编写应用层的代码。真正要发送的数据需要上层协议调用下层协议,而应用层调用传输层时,传输层(系统内核)给应用层提供的一组API统称为Socket API。 系统提供给Java程序员的Socket API主要有两组: 基于UDP的API基于TCP的API目录 一、为什么需要网络编程?——…

jmeter添加断言(详细图解)

先创建一个线程组&#xff0c;再创建一个http请求。 为了方便观察&#xff0c;我们添加两个监听器&#xff0c;察看结果树和断言结果。 添加断言&#xff1a;响应断言&#xff0c;响应断言也是比较常用的一个断言 设置响应断言&#xff1a;正常情况下响应代码是200。选择响应代…

Multisim:JFET混频器设计(含完整程序)

目录 前言实验内容一、先看作业题目要求二、作业正文IntroductionPre-lab work3.13.2 Experiment Work4.1(2)circuit setup4.1(3)add 12V DC4.1(4)set input x1 and x24.1(5)4.1(6)4.1(7)4.2(1)(2)4.2(3)4.2(4)4.3(1)(2)4.3(3) Conclusion 三、资源包内容 前言 花了好大心血完成…

NPDP产品经理知识(产品创新管理)

复习文化,团队与领导力 产品创新管理: 如何树立愿景: 如何实现产品战略=== 计划 实施产品开发: 商业化,营销计划,推广活动

1-Java SE基础

Java SE基础知识总结 背景知识 3种技术架构 Java SE&#xff1a;Java Platform Standard Edition&#xff08;标准版&#xff09;&#xff0c;完成桌面应用程序的开发&#xff0c;是其它两者的基础Java ME&#xff1a;Java Platform Micro Edition&#xff0c;开发电子消费产…

软考 系统架构设计师系列知识点之软件质量属性(1)

这个十一注定是一个不能放松、保持“紧”的十一。由于报名了全国计算机技术与软件专业技术资格&#xff08;水平&#xff09;考试&#xff0c;11月4号就要考试&#xff0c;因此8天长假绝不能荒废&#xff0c;必须要好好利用起来。现在将各个核心知识点一一进行提炼并做记录。 所…

Vue中如何进行分布式任务调度与定时任务管理

在Vue中进行分布式任务调度与定时任务管理 分布式任务调度和定时任务管理是许多应用程序中的关键功能之一。它们用于执行周期性的、异步的、重复的任务&#xff0c;例如数据备份、邮件发送、定时报告生成等。在Vue.js应用中&#xff0c;我们可以结合后端服务实现分布式任务调度…