一文搞定自动化测试

为什么需要自动化测试?

一个多人合作开发周期长的前端项目可能会出现以下问题:

  1. 代码风格各不相同
  2. 代码过度耦合,阅读和维护极其困难
  3. 新同学上手慢等等

为了解决这些问题,需要通过对核心组件进行自动化测试,保证组件职责单一,通过测试用例可以快速了解这个组件打算做什么。

通常自动化测试可分为单元测试端到端测试,单元测试我们选择 vitest 来进行讲解,端到端测试则是playwright

学习准备

项目搭建:

 

shell

复制代码

npm i -g pnpm pnpm create vite@latest vitest-demo 选择 react + ts 项目 cd vitest-demo pnpm i pnpm i vitest happy-dom @testing-library/react -D

 

json

复制代码

// package.json "scripts": { // ... "test": "vitest" },

单元测试

通过断言告诉程序你的预期

expect 用于创建断言

  • toBe: 判断基础类型是否相等(相等判断符合Object.is)
  • toEqual: 一般用于判断引用类型是否相等,断言实际值是否等于接收到的值,或者如果它是一个对象,则是否具有相同的结构。
  • toThrowError: 断言函数在被调用时是否会抛出错误,如果我们在方法中,已经有了错误的捕获,那么断言本身是无法生效
  • not: 对断言取否,相当于不等于。
  • toContain(value) :判定某个值是否存在在数组中。
  • arrayContaining(value):匹配接收到的数组,与 toEqual 结合使用可以用于判定某个数组是否是另一个数组的子集。
  • toContainEqual(value) :用于判定某个对象元素是否在数组中。
  • toHaveLength(value):断言数组的长度 。
  • toHaveProperty(value):断言对象中是否包含某个属性,针对多层级的对象可以通过 xx.yy 的方式进行传参断言。
 

ts

复制代码

// 示例函数 function sum(a, b) { if (typeof a !== 'number' || typeof b !== 'number') { throw new Error('Invalid arguments: both arguments should be numbers'); } return a + b; } // 测试代码 describe('sum function', () => { test('adds two numbers correctly', () => { expect(sum(2, 3)).toBe(5); // 判断基础类型是否相等 }); test('throws an error for invalid arguments', () => { expect(() => sum('2', 3)).toThrowError('Invalid arguments: both arguments should be numbers'); // 判断是否抛出错误 }); }); describe('array tests', () => { const arr = [1, 2, 3, 4, 5]; test('array contains a specific value', () => { expect(arr).toContain(3); // 判断数组是否包含特定值 }); test('array length is correct', () => { expect(arr).toHaveLength(5); // 判断数组长度 }); test('array is a subset of another array', () => { expect(arr).toEqual(expect.arrayContaining([2, 4])); // 判断数组是否是另一个数组的子集 }); }); describe('object tests', () => { const obj = { name: 'John', age: 30 }; test('object has a specific property', () => { expect(obj).toHaveProperty('name'); // 判断对象是否具有特定属性 expect(obj).toHaveProperty('age', 30); // 判断对象属性的值 }); });

除此之外还有自定义断言,我们可以自定义匹配器:

 

js

复制代码

// ./src/__test__/expect.test.ts import { test, expect } from "vitest" test("同步自定义匹配器", () => { const toBeBetweenZeroAndTen = (num: number) => { if (num >= 0 && num <= 10) { return { message: () => "", pass: true, }; } else { return { message: () => "expected num to be a number between zero and ten", pass: false, }; } }; // 挂载自定义匹配器 expect.extend({ toBeBetweenZeroAndTen, }); expect(8).toBeBetweenZeroAndTen(); expect(11).not.toBeBetweenZeroAndTen(); });

异步方法如何进行单测?

  1. 通过 async/await 拿到异步函数值后再进行测试
  2. 借助 Vitest 提供的 resolvesrejects 匹配器来进行异步逻辑的断言

通过 Fake Timers API 快进定时任务

Vitest 提供了一组 Fake Timers API 来跳过定时的等待时长。

  • useFakeTimers 启用假定时器
  • useRealTimers 启用真定时器
  • runAllTimers 运行所有定时器
  • runOnlyPendingTimers 只运行等待中的定时器
  • advanceTimersByTime 提前具体毫秒执行
 

ts

复制代码

// 示例函数 function delayedFunction(callback) { setTimeout(() => { callback(); }, 1000); } // 测试代码 describe('delayedFunction', () => { beforeEach(() => { jest.useFakeTimers(); // 启用假定时器 }); afterEach(() => { jest.useRealTimers(); // 启用真定时器 }); test('calls the callback after 1 second', () => { const callback = jest.fn(); delayedFunction(callback); expect(callback).not.toBeCalled(); // 断言回调函数尚未被调用 jest.runAllTimers(); // 运行所有定时器 expect(callback).toBeCalled(); // 断言回调函数已被调用 expect(callback).toHaveBeenCalledTimes(1); // 断言回调函数只被调用一次 }); test('does not call the callback before 1 second', () => { const callback = jest.fn(); delayedFunction(callback); jest.advanceTimersByTime(500); // 提前500毫秒执行 expect(callback).not.toBeCalled(); // 断言回调函数尚未被调用 }); test('calls the callback only for pending timers', () => { const callback1 = jest.fn(); const callback2 = jest.fn(); delayedFunction(callback1); delayedFunction(callback2); jest.runOnlyPendingTimers(); // 只运行等待中的定时器 expect(callback1).toBeCalled(); // 断言callback1已被调用 expect(callback2).not.toBeCalled(); // 断言callback2尚未被调用 }); });

通过 Mock 替代不需要关注的逻辑

一个文件可能会包含一些测试环境没有的 API 或者全局变量,或者不在我们测试范围内的外部文件,我们需要使用 mock 来模拟它们进行测试。

全局 mock
 

js

复制代码

vitest.mock(path, moduleFactory)

  • path:需要 mock 的文件路径
  • moduleFactory: 这个模块的工厂函数
单次 mock
 

js

复制代码

vitest.doMock(moduleName, factory, options)

mock 函数

vitest.fn 用于 mock 一个空函数,它会默认返回 undefined,当然我们也可以传入两个类型来控制它的入参和回参内容,例如jest.fn<string, string>()

vitest.spyon可以创建一个和jest.fn类似的 mock 函数,不同的是它可以追踪目标函数的调用,使得它的入参和回参与需要 mock 的函数是自动匹配的。

通过 React Testing Library 进行 DOM 查询

Vitest 的基础断言通常用于纯 JavaScript 逻辑的断言,对 DOM 元素进行单元测试使用React Testing Library

两者的关系为:

  • Vitest 提供测试方法:断言、Mock 、SpyOn 等方法。
  • RTL 主要提供 React 组件渲染, DOM 解析,DOM 的事件模拟。

happy-dom , 它的作用是在测试的运行环境 node 下提供对 web 标准的模拟实现。我们在开发 React 的运行环境时浏览器与测试的运行环境不一致,比如 window,document, web存储的API 在 node 运行时是不存在的,这影响了测试。 happy-dom 完成了对这些标准的补充。

在进行页面元素的查询之前需要进行页面元素的渲染:

 

jsx

复制代码

import {render} from '@testing-library/react' test("test", () => { // 通过 render 方法渲染 render(<App />); // screen api 进行查询 screen[...] // 需要啥用啥 }

常见的页面查询 api 及其分类

一个查询方法一般由两部分组成:行为分类 + 参照物分类

行为分类

行为角度上,查询 API 可以包含三种类别(getBy, queryBy, findBy),它们各自又包含单查多查(getAllBy, queryAllBy, findAllBy)。从字面意思就可以看出单查是只能查一个,多查则会查全部。

  • Get:返回查询的匹配节点,如果没元素匹配,则会报错(针对单查如果查到多个也会报错);
  • Query:返回查询的匹配节点,如果没有元素匹配会返回 null,但是不会报错(同样针对单查,如果查到多个匹配元素也会报错)
  • Find:返回一个 Promise,默认超时时间为 1000 ms, 如果没有元素匹配或者查找超时,Promise 状态切为 reject(同样针对单查,如果查到多个元素,也会返回 reject)。
通过 waitfor API 测试异步函数

React testing library 提供有一个 waitfor 的 API。

waitfor接收两个参数,第一个是需要重复执行的回调函数,我们可以在其中查询元素并且断言,waitfor 会根据设定(或者默认)的超时时间和执行间隔来重复执行回调。第二个参数是可以配置的数据,比如说超时时间(timeout)、执行间隔(interval),通过这个参数我们就可以自定义我们需要的超时场景。

参照物分类

我们详细了解一下 role 分类,剩下的比较常用的分类大伙可以去官网查看

role - 角色

要理解角色的含义,首先我们需要来了解一个 W3C 语义 ---- ARIA。

ARIA (Accessible Rich Internet Applications) 是一组属性,用于定义使残障人士更容易访问 Web 内容和 Web 应用程序(尤其是使用 JavaScript 开发的应用程序)的方法。

我们使用的 div、button 等标签,即使没有加任何属性,也有一个隐性的 ARIA role 属性来表示它的语义,就拿 button 为例,<button>按钮</button> 其实可以看作是 <button role="button">按钮</button>,这个就是 role 查询。

除了基础的角色 role 外,W3C 在 ARIA 语义的提案中还包含了 aria 属性,这个语义表明 role 语义的状态和属性,比如 “按压” 的 button, "隐藏" 的 button 等

90d2ff7f4c50c44bfabd9c51f3bced21.png

  • aria-hidden: 不在 DOM 树上访问的元素;
  • aria-selected: 元素是否被选中;
  • aria-checked: 元素是否被勾选;
  • aria-current: 当前选中的元素;
  • aria-pressed: 被按压的元素;
  • aria-expanded:元素是否被展开;
  • aria-level: 区域的等级,值得一提的是,h1 - h6 会有默认的aria-level属性,值对应1-6;
  • aria-describedby: 可以通过描述来定位额外的元素。
其它参照物
  • 标签文本:针对label标签的text查询

const label = screen.getByLabelText("testLabel")

  • 占位符文本(placeholdertext):通过placeholder来查询

const placeholderInput = screen.getByPlaceholderText( "a query by placeholder" );

  • 表单value(displayValue):根据表单元素的值来查询

const valueInput = screen.getByDisPlayValue("a query by value")

如何进行 DOM 断言?

页面可见断言
  • toBeEmptyDOMElement:标签之间是否有可见内容, 即使是空格也会失败;
  • toBeVisible:是否可见,从用户直接观察的角度看能否可见;
  • toBeInTheDocument:是否存在在文档中,document.body 是否存在这个元素。
 

ts

复制代码

// 示例函数 function createEmptyElement() { return document.createElement('div'); } function createElementWithContent() { const div = document.createElement('div'); div.textContent = 'Hello, Jest!'; return div; } function createHiddenElement() { const div = document.createElement('div'); div.style.display = 'none'; return div; } // 测试代码 describe('DOM element tests', () => { test('element is empty', () => { const element = createEmptyElement(); expect(element).toBeEmptyDOMElement(); // 断言元素为空 }); test('element has visible content', () => { const element = createElementWithContent(); expect(element).toBeVisible(); // 断言元素可见 }); test('element is not visible', () => { const element = createHiddenElement(); expect(element).not.toBeVisible(); // 断言元素不可见 }); test('element is in the document', () => { const element = createEmptyElement(); document.body.appendChild(element); expect(element).toBeInTheDocument(); // 断言元素存在于文档中 }); });

表单验证断言
  • toBeDisabled :检查元素是否通过 disable 属性判断,而不是 aria-disabled;
  • toBeEnabled: 是否未被禁用,等同于 .not.toBeDisabled
  • toBeRequired: 元素是否必填;
  • toHaveFocus: 元素是否聚焦;
  • toBeChecked: checkbox 或者是 radio 是否被选中;
  • toHaveFormValues:验证整体表单的值是否和预期值匹配;
  • toHaveValue:与 toHaveFormValues 类似,不过不同的是 toHaveValue 验证某个单独的表单元素,而不是全部。
代码层面验证
  • toHaveAttribute: 匹配元素是否具备某个值的属性;
  • toHaveClass: 匹配元素在类属性中是否包含某个类;
  • toHaveStyle: 匹配元素是否具有对应样式,需要注意的是,这个是精准非模糊匹配,例如 display: none 无法匹配 display:none;color:#fff;

通过 fireEventuserEvent 模拟事件绑定触发

React Testing Library 提供了两种手段来模拟,fireEventuserEvent

我们应该尽量避免使用 fireEvent,而是使用 userEvent

 

js

复制代码

fireEvent[eventName](node: HTMLElement, eventProperties: Object)

  • eventName: 事件名
  • node: 查询出来的对象
  • eventProperties: 描述这个具体事件的属性

通常对于事件的用例,我们会使用到 Vitest 提供的 mock 事件,以及 toBeCalled 和 toBeCalledTimes 两个断言,toBeCalled 用来判断 mock 事件是否被调用,而 toBeCalledTimes 用来判断 mock 事件被调用的次数。

userEvent 的实现,除了模拟传入实例直接需要的 click 外,它还触发了这个元素聚焦和失焦,就不像 fireEvent ,只是简单返回模拟的事件

怎么测试 React Hook?

  1. 通过直接测试调用 hook 组件的方式来完成这部分用例

  2. testing-library 提供了一个 renderHook 的方法来帮我们实现。

第一种方式对于一些公共 hook 可能会需要专门建一个组件进行测试,这会导致写很多与业务无关的代码,但测试的健壮性更强。

通过快照保证组件 UI 完整

快照测试和它的字面意思一样,通过快速(简单)拍出的照片来测试,它是将我们需要判定的元素的内容存储下来,生成一个 snapshots 目录用来存放快照文件,在下一次匹配时,会判断两次的结果能否匹配,从而达到从整体维度保证组件功能完成的能力。

快照测试更适合使用在不轻易改变,甚至不会去改变的公共逻辑中

React Testing library 中提供了快照测试的能力,我们只需要使用它提供给我们的 toMatchSnapshot 断言就好

如何更新快照?

直接在控制台中输入 u 来更新快照

端对端测试

什么是端对端测试?

E2E(End-to-End)测试是一种软件测试方法,用于测试整个应用程序的工作流程,以确保整个系统按预期工作。它不需要基于项目,有页面就可以测试,通常由测试人员编写。

这通常涉及从用户界面(UI)开始,覆盖系统各个层,比如业务逻辑层、API/服务层、数据层。

E2E 测试是从用户的角度出发,模拟真实世界场景来进行的,它有以下特点:

  • 全面性: 覆盖整个应用程序的所有交互和数据流。
  • 实用性: 能够捕获整体系统行为,而不仅仅是单个组件。
  • 复杂性: 可能会涉及多个系统或子系统。

安装

 

shell

复制代码

pnpm create playwright

运行:

开两个终端分别运行

 

shell

复制代码

pnpm run dev npx playwright test --ui

然后就可以看见 playwright 的可视化界面了。

befb8ca0a1fac2504ccfc8e1753f69a6.png

E2E 测试用例编写原则

在决定好如何组织 E2E 测试代码后,我们在写具体测试用例时,尽可能遵从以下原则:

  1. 测试用户可见行为:测试应该验证页面中对于用户可见的部分,避免依赖实现细节(比如对于用户不可见的函数名称,css 类名等)
 

ts

复制代码

// 👎 test('should submit when button is clicked', async ({ page }) => { await page.goto('http://example.com'); await page.locator('button.submit-icon').click(); // 依赖于 CSS 选择器,如果后续更改了类名,测试用例会失败 }); // 👍 test('should submit when button is clicked', async ({ page }) => { await page.goto('http://example.com'); await page.getByRole('button', {name: '提交'}).click(); // 依赖于用户可见的文本,不依赖实现细节 });

  1. 尽可能将测试隔离:每个测试都应该完全与其他测试隔离,并且应该独立运行,具有自己的 local storage、session storage、数据、Cookie 等。测试隔离提高了可重复性,使调试更容易,并防止级联测试失败。

  2. 避免测试第三方依赖:只测试项目中我们可控的部分,不测试我们无法控制的外部网站和 API,避免耗费时间,还可能导致测试失败
    假如项目中依赖了外部 API,可以使用 Playwright Network API 对响应进行 mock:

 

ts

复制代码

await page.route('**/api/fetch_data_third_party_dependency', route => route.fulfill({ status: 200, body: mockData, })); await page.goto('https://example.com');

关于其他 playwright 的 api,大家可以到官网去学习。

可深究的方向

  1. 自动化测试覆盖率的统计。
  2. 项目自动化测试的持续集成。
  3. 怎么覆盖滚动等复杂交互场景?
  4. 怎样才算一个好的测试代码?

 总结

如果你对此文有任何疑问,如果你也需要接口项目实战,如果你对软件测试、接口测试、自动化测试、面试经验交流感兴趣欢迎加入我们,加入方式在文章的最后面

  自动化测试相关教程推荐:

2023最新自动化测试自学教程新手小白26天入门最详细教程,目前已有300多人通过学习这套教程入职大厂!!_哔哩哔哩_bilibili

2023最新合集Python自动化测试开发框架【全栈/实战/教程】合集精华,学完年薪40W+_哔哩哔哩_bilibili

测试开发相关教程推荐

2023全网最牛,字节测试开发大佬现场教学,从零开始教你成为年薪百万的测试开发工程师_哔哩哔哩_bilibili

postman/jmeter/fiddler测试工具类教程推荐

讲的最详细JMeter接口测试/接口自动化测试项目实战合集教程,学jmeter接口测试一套教程就够了!!_哔哩哔哩_bilibili

2023自学fiddler抓包,请一定要看完【如何1天学会fiddler抓包】的全网最详细视频教程!!_哔哩哔哩_bilibili

2023全网封神,B站讲的最详细的Postman接口测试实战教学,小白都能学会_哔哩哔哩_bilibili

  总结:

 光学理论是没用的,要学会跟着一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。

​​​

​​​

如果对你有帮助的话,点个赞收个藏,给作者一个鼓励。也方便你下次能够快速查找。

如有不懂还要咨询下方小卡片,博主也希望和志同道合的测试人员一起学习进步

在适当的年龄,选择适当的岗位,尽量去发挥好自己的优势。

我的自动化测试开发之路,一路走来都离不每个阶段的计划,因为自己喜欢规划和总结,

测试开发视频教程、学习笔记领取传送门!!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

S2B2C商城系统如何选择?

近年来&#xff0c;S2B2C商城模式被众多企业所青睐&#xff0c;其场景应用包括装修建材、家居用品、汽车、房产、家电、零售、生活用品等各个领域。那么&#xff0c;对于目前还没涉足电商领域的企业来说&#xff0c;SB2B2C商城系统如何选择呢&#xff1f;我们可以从推出时间、开…

跨式期权组合策略是什么?怎么使用跨式期权?

今天带你了解跨式期权组合策略是什么&#xff1f;怎么使用跨式期权&#xff1f;跨式期权策略&#xff0c;这个看似高深莫测的金融工具&#xff0c;实际上是一种非常灵活且实用的投资策略。它以其独特的风险收益特点&#xff0c;吸引了越来越多的投资者关注。 跨式期权组合策略是…

RedHat9 | 防火墙配置与管理

RedHat9中默认安装Firewalld&#xff0c;也可安装iptables。但是两者不可同时运行。 软件或程序主要作用firewalld、iptables策略限制MAC、IP、PORT、ARPSELinux上下文、布尔值、端口软件权限读写执行权限文件系统rwx、隐藏权限、ACL 1、Netfilter 包过滤防火墙工作在TCP/IP的…

【霸王餐系统】搭建部署,可设置二级分销

前言&#xff1a; 霸王餐项目通常是由外卖平台或商家发起的一种营销策略&#xff0c;旨在通过提供低成本甚至免费的外卖来吸引消费者&#xff0c;从而增加销量、优化评价并扩大市场影响力。这种项目往往能够实现平台、商家、推广者和消费者四方共赢的局面。 一、项目优势 市…

uniapp 展示地图,并获取当前位置信息(精确位置)

使用uniapp 提供的map标签 <map :keymapIndex class"container" :latitude"latitude" :longitude"longitude" ></map> 页面初始化的时候&#xff0c;获取当前的位置信息 created() {let that thisuni.getLocation({type: gcj02…

pepy - Python 包下载量统计工具站

文章目录 一、关于 pepy站点使用显示下载量 二、代码贡献依赖环境配置启动环境 三、架构和模式四、常问问题 一、关于 pepy 官网&#xff1a;https://www.pepy.techgithub : https://github.com/psincraian/pepy 前端&#xff1a;https://github.com/psincraian/pepy-front 站…

Linux文本三剑客 awk 和 grep

awk 前言 AWK是一种优良的文本处理工具。它不仅是 Linux中也是任何环境中现有的功能最强大的数据处理引擎之一。 Linux中最常用的文本处理工具有grep&#xff0c;sed&#xff0c;awk。行内将之称为文本三剑客&#xff0c;就功能量和效率来看&#xff0c;awk是当之无愧的文本三…

智能盒子如何检测进气压力传感器?

进气压力传感器是一种用于测量发动机进气系统中压力的传感器。安装在发动机的进气管路或进气歧管上&#xff0c;用于监测进气压力的变化。进气压力传感器的作用是将测量到的压力信号转换为电信号&#xff0c;以便发动机控制单元(ECU)可以根据压力变化来调整燃油喷射量、点火时机…

git版本控制流程

个人名片 &#x1f393;作者简介&#xff1a;java领域优质创作者 &#x1f310;个人主页&#xff1a;码农阿豪 &#x1f4de;工作室&#xff1a;新空间代码工作室&#xff08;提供各种软件服务&#xff09; &#x1f48c;个人邮箱&#xff1a;[2435024119qq.com] &#x1f4f1…

开启数字化校园时代:智慧教育的引领者

在当前数字化时代&#xff0c;智慧教育已经成为引领未来的热门议题。随着科技的不断进步和教育观念的演进&#xff0c;数字化校园正逐渐变为现实。作为教育界的领导者&#xff0c;我们积极践行智慧教育理念&#xff0c;为学生们营造一个更智能、高效的学习环境。 教育是每个国家…

零售业上云为什么首选谷歌云

零售业是国民经济的重要组成部分&#xff0c;在促进经济发展、改善人民生活水平方面发挥着重要作用。零售业也是一个竞争激烈的行业&#xff0c;零售企业需要不断创新经营方式、提高服务质量才能在竞争中立于不败之地。 近年来&#xff0c;中国企业在品牌出海方面&#xff0c;一…

常见电子元件封装

常用电子元件封装 本文摘要 本文主要讲述元件封装&#xff0c;我们想要用元件&#xff0c;当然要考虑到他的形状尺寸了&#xff0c;如果太大与结构冲突用不了&#xff0c;太小功率不满足使用在电路中而无法使用&#xff0c;每种元件也因为不同封装可以承受电压等级&#xff0c;…

实现数字化教育梦想:数字化校园解决方案的奇妙魅力

数字化校园解决方案正在以惊人的速度改变着传统教育的面貌&#xff0c;为教育事业注入了新的生机与活力。在这个数字时代&#xff0c;学校和教育机构面临着前所未有的挑战&#xff0c;而数字化校园解决方案则成为了应对这些挑战的利器。它集成了先进的技术和创新的教育理念&…

时间处理获取交易日(考虑兼容性问题)

在获取交易日时间的处理上&#xff0c;出现了苹果14不兼容的问题&#xff0c;就这个问题记录下。 一、获取交易日的代码 封装了一个js文件&#xff0c;在untils目录下&#xff0c;先看代码&#xff0c;然后我讲下思路。 // 获取节假日数据 import { getCalendarHolidays } …

# 常用刀具钢材介绍

常用刀具钢材介绍 文章目录 常用刀具钢材介绍1、M390 钢材详细介绍1.1、关键特性1.2、应用 2、 VG10 钢材详细介绍2.1、关键特性2.2、应用 3、N690 钢材详细介绍3.1、关键特性3.2、应用 4、D2, D53, 和 7Cr13MoV 钢材对比4.1、D2 钢材4.2、D53 钢材4.3、7Cr13MoV 钢材4.4、对比…

奇怪的bug

奇怪的bug 合集 1.不可见字符集问题 起因是在自己做小项目的时候&#xff0c;通过lombok的data注解&#xff0c;默认生成实体类的get set方法 但是在某个方法中获取一个属性值的时候显示找不到该属性值的get方法&#xff0c;具体直接贴图 我以为是lombok的配置问题&#xff0c…

opencv--使用opencv实现旋转角度的模板匹配

下面的例子是简单的使用opencv 实现的模板匹配流程&#xff0c;其中时间性能和精确度还需要调整&#xff0c;如果直接使用会出问题&#xff0c;所以这个只是例子&#xff0c;根据代码原理可以实现尺度变化的模板匹配和旋转尺度变化同时&#xff0c;具体根据实现的旋转代码进一步…

手持式凝血分析仪条码二维码高效读取解决方案

在医疗行业中&#xff0c;凝血分析仪是一种关键设备&#xff0c;主要用于快速、准确地检测血液凝固功能&#xff0c;同时不需要复杂的样品制备&#xff0c;并且可以在几分钟内获得患者的凝血功能信息&#xff0c;对疾病的诊断和治疗具有重要意义。医疗技术和科技的飞速发展&…

三篇卫星切换的论文

目录 一、Energy-Aware Satellite Handover based on Deep Reinforcement Learning 1、题目翻译 2、来源 3、内容 二、A Reliable Handover Strategy with Second Satellite Selection in LEO Satellite Networks 1、题目翻译 2、来源 3、内容 三、User Grouping-Based…

揭秘:义乌理阳的跨境选品师项目

在全球经济一体化的今天&#xff0c;跨境电商已成为各国贸易的重要组成部分&#xff0c;而选品师作为其中的关键角色&#xff0c;扮演着挑选优质商品的重要角色。在中国义乌&#xff0c;一家名为理阳信息咨询服务有限公司备受关注&#xff0c;因其据称拥有跨境选品师项目而备受…