记录使用vue-test-utils + jest 在uniapp中进行单元测试

这篇具有很好参考价值的文章主要介绍了记录使用vue-test-utils + jest 在uniapp中进行单元测试。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前情

  • 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 = value
  target.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文章来源地址https://www.toymoban.com/news/detail-742044.html

到了这里,关于记录使用vue-test-utils + jest 在uniapp中进行单元测试的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • vitest 单元测试配合@vue/test-utils 之 axios 篇

    vitest 是由 vite 提供支持的极速单元测试框架,VueTestUtils 是 Vue.js 的官方测试实用程序库,Axios 是一个基于 promise 的网络请求库,以上均为各自官网对其的描述 项目中使用 axios 是非常常见的,所以我们可以对他做一个单元测试,在 test-utils 的文档中提到除了 jest.mock()还可以使

    2024年02月19日
    浏览(28)
  • vitest单元测试配合@vue/test-utils之组件单元测试篇

    vitest 是由 vite 提供支持的极速单元测试框架,VueTestUtils 是 Vue.js 的官方测试实用程序库,vitest 本身是不支持单元组件测试的,需要配合 test-utils 来完成组件单元测试,安装与基本 API 就不再赘述,学会阅读文档与查找资料是一个程序员的基本功 demo 由一个组件和测试文件组成

    2024年03月16日
    浏览(39)
  • 如何使用Jest进行单元测试

    Jest 是一种流行的 JavaScript 测试框架,它具有易用性和高效性。Jest 支持测试各种 JavaScript 应用程序,包括 React、Vue、Node.js 等。在本文中,我们将介绍如何使用 Jest 进行单元测试。 ## 1. 安装 Jest 首先,我们需要在项目中安装 Jest。可以使用 npm 或 yarn 安装 Jest: ``` npm install

    2024年02月10日
    浏览(30)
  • Testing Angular, VueJS, and React Components with Jest

    作者:禅与计算机程序设计艺术 在过去的几年里,React、Angular和Vue等前端框架都获得了越来越多开发者的青睐,并且取得了不俗的成绩。这些前端框架的出现给前端开发领域带来了许多新鲜的机会。特别是在面对复杂业务需求时,测试驱动开发(TDD)方法对于保证项目质量至

    2024年02月06日
    浏览(32)
  • 使用 Jest 在 Visual Studio Code 中进行更好的单元测试

    目录 前言: 使用 Jest 扩展显著改善测试流程 1.自动启动 Jest 测试 2. 显示单个失败/通过的测试用例 允许调试单元测试 在文件中显示代码覆盖率 结论 前言: Jest是一个流行的JavaScript测试框架,它提供了简洁、灵活和强大的工具来编写和运行单元测试。在Visual Studio Code(VS C

    2024年02月13日
    浏览(41)
  • uniapp Vue 使用 sip.js进行语音通话视频通话

    下载或者安装 sip.js 到 uniapp 项目,APP 端在 menifest.json 中配置麦克风权限 menifest.json 中 app 权限配置选中: android.permission.RECORD_AUDIO android.permission.MODIFY_AUDIO_SETTINGS sip.js 低版本 如 V0.13.0 版本的写法 sip.js 高版本如 V0.21.2 用法 (参数同上,只列出 methods 里的部分) APP模式下检测麦

    2024年02月13日
    浏览(120)
  • test ui-01-UI 页面测试 Selenium/Appium/Cypress/TestCafe/Playwright/WebDriverIO/Nightwatch/Puppeteer/Jest

    UI测试(用户界面测试)是软件测试中的一个重要方面,其主要目的是确保用户界面的正常运作,并验证用户可以按照设计的方式与应用程序进行交互。 UI测试通常涉及检查图形用户界面(GUI)元素的正确性、响应性和用户体验等方面。 在测试过程中,测试人员会模拟用户与

    2024年01月17日
    浏览(28)
  • 使用Simulink Test进行单元测试

    本文摘要:主要介绍如何利用Simulink Test工具箱,对模型进行单元测试。内容包括,如何创建Test Harness模型,如何自动生成excel格式的测试用例模板来创建测试用例,如何手动填写excel格式的测试用例模板来手动创建测试用例。 单元测试的目的 创建完模型后,我们需要验证模型

    2024年02月16日
    浏览(33)
  • SpringBoot 如何使用 Spring Test 进行集成测试

    在开发过程中,单元测试是不可或缺的,它可以帮助我们及时发现代码的问题并进行修复,从而提高代码的质量和可维护性。但是,单元测试只能测试单个方法或类的功能,无法测试多个模块之间的交互和整个应用程序的功能。因此,为了确保应用程序的正确性和健壮性,我

    2024年02月10日
    浏览(34)
  • [C++] 基础教程 - 如何使用google test进行单元测试

    https://download.csdn.net/download/u011775793/88601877 单元测试是一种软件测试方法,用于测试代码中的最小可测试单元。在软件开发中,我们通常将代码分解为多个模块或类,每个模块或类都有自己的功能和行为。单元测试的目的是确保每个模块或类都能正常工作,不会影响其他模块或

    2024年02月04日
    浏览(33)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包