当前位置: 首页 > news >正文

单元测试:为工程质量保驾护航

单元测试

单元测试是软件开发过程中确保代码质量和正确性的关键手段。它指的是对软件中的最小可测试单元(通常是函数或方法)进行验证,确保其行为符合预期。

基本概念

  • 单元测试:验证软件中最小单元(通常是函数或方法)的正确性,确保其独立性和可重复性。
  • 测试用例:描述一组输入和预期输出,用于验证软件单元的行为是否正确。
  • 测试覆盖率:衡量单元测试覆盖程序代码量的指标,分为行覆盖率、分支覆盖率和路径覆盖率等。

重要性

  • 早期发现 BUG:在开发过程中早期发现和修复缺陷,降低修复成本。
  • 代码重构保障:在进行代码重构时确保现有功能不被破坏。
  • 文档和示例:单元测试可以作为代码的实际使用示例,提供良好的文档支持。
  • 持续集成:单元测试是持续集成和持续交付的基础,确保每次代码变更不会引入新的问题。

常用技术

  1. 断言 ((\textit{Assertions})):用于验证代码执行结果是否符合预期。
  2. 测试框架
    • Python:unittest, pytest
    • Java:JUnit
    • JavaScript:Jest, Mocha
  3. Mock 测试:用于模拟和隔离外部依赖(如数据库、网络服务)。
    • Python:unittest.mock
    • Java:Mockito
    • JavaScript:Sinon.js

功能测试

功能代码

我们使用 Python 实现以下 UserServiceEmailServiceUserServiceWithEmail 三个类,来演示单元测试的基本概念。

# user_service.py
class UserService:def __init__(self):self.users = {}  # 使用字典来存储用户信息def add_user(self, user_id, name):# 添加用户if user_id in self.users:raise ValueError("User ID already exists")self.users[user_id] = namedef get_user(self, user_id):# 查找用户return self.users.get(user_id, None)def delete_user(self, user_id):# 删除用户if user_id in self.users:del self.users[user_id]else:raise ValueError("User ID does not exist")# email_service.py
class EmailService:def send_email(self, email_address, subject, content):# 模拟发送电子邮件print(f"Sending email to {email_address} with subject {subject}")return True# email_decorator.py
class UserServiceWithEmail(UserService):def __init__(self, email_service):super().__init__()self.email_service = email_servicedef add_user(self, user_id, name, email_address):super().add_user(user_id, name)self.email_service.send_email(email_address, "Welcome!", "Thank you for registering!")

单测代码

单元测试的目标是确保每个单独的函数或方法在与系统其他部分隔离的情况下工作正常,所以需要码验证了各种可能的操作场景。

接下我们分别对 UserServiceUserServiceWithEmail 进行测试。

# test_user_service.py
import unittest
from user_service.py import UserService
from email_service.py import EmailService
from email_decorator.py import UserServiceWithEmail
from unittest.mock import MagicMockclass TestUserService(unittest.TestCase):def setUp(self):# 说明:每个测试方法运行前都会执行 `setUp` 方法,初始化一个 `UserService` 实例,确保每个测试在相同的初始状态下进行。self.service = UserService()def test_add_user(self):# 说明:测试添加用户功能。添加一个用户后,使用 `get_user` 方法检索该用户,并验证返回结果是否与添加的一致。self.service.add_user(1, "John")self.assertEqual(self.service.get_user(1), "John")def test_add_user_existing_id(self):# 说明:测试添加重复用户 ID 的情况。第一次添加用户成功后,尝试用相同的 ID 添加新用户,验证是否抛出 `ValueError` 异常。self.service.add_user(1, "John")with self.assertRaises(ValueError):self.service.add_user(1, "Jane")def test_get_user_non_existing(self):# 说明:测试获取不存在的用户。调用 `get_user` 方法查询一个不存在的用户 ID,验证返回值是否为 `None`。self.assertIsNone(self.service.get_user(999))def test_delete_user(self):# 说明:测试删除用户功能。首先添加一个用户,然后删除该用户,验证该用户是否已经被成功删除(查询时返回 `None`)。self.service.add_user(1, "John")self.service.delete_user(1)self.assertIsNone(self.service.get_user(1))def test_delete_user_non_existing(self):# 说明:测试删除不存在的用户。尝试删除一个不存在的用户,验证是否抛出 `ValueError` 异常。with self.assertRaises(ValueError):self.service.delete_user(999)class TestUserServiceWithEmail(unittest.TestCase):def setUp(self):self.email_service = EmailService()# 说明:使用 `MagicMock` 对象 Mock(模拟) `send_email` 方法,使其在测试过程中返回 `True` 而不是实际发送电子邮件。初始化测试对象时,将 Mock 对象传入测试函数。self.email_service.send_email = MagicMock(return_value=True)self.service = UserServiceWithEmail(self.email_service)def test_add_user_sends_email(self):# 说明:测试添加用户并发送欢迎电子邮件的功能。添加用户后,验证 `send_email` 方法是否被调用一次,且参数正确。user_id = 1name = "John"email_address = "john@example.com"self.service.add_user(user_id, name, email_address)self.email_service.send_email.assert_called_once_with(email_address, "Welcome!", "Thank you for registering!")def test_add_user_existing_id(self):# 说明:测试在电子邮件版本的用户服务中添加重复用户 ID 的情况。预计会抛出 `ValueError`。self.service.add_user(1, "John", "john@example.com")with self.assertRaises(ValueError):self.service.add_user(1, "Jane", "jane@example.com")def test_delete_user(self):# 说明:测试删除用户功能。同样地,先添加用户,再删除,最后验证该用户是否已经被成功删除。self.service.add_user(1, "John", "john@example.com")self.service.delete_user(1)self.assertIsNone(self.service.get_user(1))if __name__ == "__main__":unittest.main()

Mock 测试

基本概念

Mock 测试是一种在软件测试中模拟对象或行为的技术。在测试某个单元(通常是一个函数或类)时,通过创建“虚拟对象”来模拟系统中的真实对象或依赖,替代它所依赖的其他组件,以便隔离待测试单元并专注于其自身的逻辑。通过使用 mock 对象,可以控制这些外部依赖的行为和状态,从而确保测试的确定性和一致性。

主要作用

  • 隔离测试:确保测试只关注目标组件,而不受其他组件或外部系统的影响。
  • 控制行为:可以设定模拟对象的返回值或行为,以测试不同场景。
  • 提高效率:避免与数据库、网络等真实服务的交互,提高测试速度。

常见场景

Mock 测试常用于单元测试,帮助开发者确保代码在预期条件下的表现。

  • 替代数据库调用,以避免对实际数据库的写操作。
  • 模拟网络请求,测试响应处理逻辑。
  • 模拟复杂的对象或系统行为,以简化测试环境。

常用的 mock 框架有 Python 的 unittest.mock 和 JavaScript 的 Jest Mocks。

Mock 对象

上文的 TestUserServiceWithEmail 中,MagicMock 对象模拟了 EmailServicesend_email,演示了 Mock 方法。此处我们再举一个例子,演示一下 Mock 对象

假设我们有一段依赖外部服务的代码:

# user_service.py
class UserService:def __init__(self, api_client):self.api_client = api_clientdef get_user_data(self, user_id):# 依赖 api_client 所调用的外部服务response = self.api_client.get(f"/users/{user_id}")if response.status_code == 200:return response.json()else:raise ValueError("User not found")

我们可以使用 Mock 对象来模拟 api_client 的行为,以便测试 UserServiceget_user_data 方法:

# test_user_service.py
import unittest
from user_service import UserService
from unittest.mock import MagicMockclass TestUserService(unittest.TestCase):def setUp(self):# 使用 MagicMock 对象模拟 api_clientself.api_client = MagicMock()self.user_service = UserService(self.api_client)def test_get_user_data_success(self):# 让 user_service.get_user_data 中,模拟的外部服务调用(api_client.get)直接返回成功(200)的结果self.api_client.get.return_value.status_code = 200self.api_client.get.return_value.json.return_value = {"id": 1, "name": "John"}user_data = self.user_service.get_user_data(1)self.assertEqual(user_data, {"id": 1, "name": "John"})def test_get_user_data_failure(self):# 让 user_service.get_user_data 中,模拟的外部服务调用(api_client.get)直接返回失败(404)的结果self.api_client.get.return_value.status_code = 404with self.assertRaises(ValueError):self.user_service.get_user_data(1)if __name__ == '__main__':unittest.main()

使用 @patch

使用 @patch 装饰器可以进一步简化和清晰化测试逻辑。它允许我们在测试之前设置 Mock 对象,并在测试结束后自动恢复原始对象,减少手动处理的复杂性。

以下我们使用 @patch 装饰器重构上文中 TestUserServiceWithEmail 的测试代码:

# test_user_service.py
import unittest
from email_service import EmailService
from email_decorator import UserServiceWithEmail
from unittest.mock import patchclass TestUserServiceWithEmail(unittest.TestCase):...# 说明:使用 `@patch` 装饰器 Mock `send_email` 方法,使其在测试过程中返回 `True` 而不是实际发送电子邮件。初始化测试对象时,将 Mock 对象传入测试函数。@patch('email_service.EmailService.send_email', return_value=True)def setUp(self, mock_send_email):self.mock_send_email = mock_send_emailself.service = UserServiceWithEmail(EmailService())...

@patch 优势

使用 @patch 装饰器重构后的代码与之前手动 Mock 的代码相比,具有以下优点和特点:

  1. 简化了 Mock 对象的创建和恢复:

    • 之前:需要在 setUp 方法中手动创建 Mock 对象,并在测试方法中调用它。
    • 现在:通过 @patch 装饰器,可以直接在测试方法中获得 Mock 对象,同时测试结束后自动恢复原始对象,减少了手动操作和错误可能性。
    # 使用 MagicMock 手动 Mock
    self.email_service.send_email = MagicMock(return_value=True)# 使用 @patch 自动 Mock
    @patch('email_service.EmailService.send_email', return_value=True)
    
  2. 增强代码清晰度:

    • 之前:在进行 Mock 时,代码中需要额外维护 Mock 对象的状态。
    • 现在: @patch 装饰器使得测试代码更加简洁和直观,将 Mock 逻辑与实际测试逻辑解耦。
    # 使用 MagicMock 手动 Mock
    self.email_service.send_email.assert_called_once_with(...)# 使用 @patch 自动 Mock
    self.mock_send_email.assert_called_once_with(...)
    
  3. 集中管理 Mock:

    • 之前:需要在每个测试方法中手动处理 Mock 对象。
    • 现在:通过 @patch 装饰器,可以在类级别或方法级别集中管理 Mock 对象,使得 Mock 配置更容易理解和维护。

此外使用 @patch 还有以下特点:

  • 上下文管理@patch 的另一优势在于它能够管理 Mock 对象的生命周期,上下文管理器特性使得在大的测试类或测试文件中不会出现混乱的状态问题。
  • 测试隔离性:使用 @patch 时,各个测试方法之间的 Mock 状态是相互隔离的。这确保了一个测试方法中的 Mock 不会影响其他测试方法,增强了测试的可靠性。

使用 @patch 装饰器可以显著改善单元测试代码的简洁性和可维护性,使 Mock 对象的配置和恢复更为直观和自动化。它有助于提高测试代码的清晰度和隔离性,特别适用于复杂的测试场景和依赖多个外部服务的系统。掌握和应用这一技术,是提高代码质量和测试效率的重要工具。

Mock 测试的思考

Mock 测试使我们可以隔离单元测试,确保单元功能的正确性,不受外部依赖的影响,但也带来一些挑战和需要注意的事项:

  • 过度 Mock:过度依赖 Mock 可能导致测试体系与实际运行环境脱节。应当只 Mock 那些外部依赖,而不是系统内部逻辑。
  • 行为验证 vs 状态验证:Mock 更关注行为验证(验证某些调用是否发生),而非状态验证(验证某些状态值)。在实际测试中,二者需要平衡使用。
  • 保持一致性:Mock 对象的行为应当尽可能与真实对象一致,以避免测试结果和实际情况差异过大。

最佳实践

Arrange-Act-Assert 模式

  • Arrange:设置测试场景和准备所需的状态。
  • Act:调用待测试的方法或函数。
  • Assert:验证结果是否符合预期。

单测技巧

  1. 使用 setUptearDown:使用 setUptearDown 方法来准备和清理测试环境,减少重复代码。确保每个测试在一个确定的状态下开始。
  2. 使用 assertRaises:在异常处理中使用 assertRaises 方法来断言代码会在特定情况下抛出预期的异常。例如,尝试添加已经存在的用户 ID,需要抛出 ValueError
  3. 状态验证 vs 行为验证
    • 状态验证:通过检查方法调用后的状态确保系统行为正确。
    • 行为验证:用 mock 验证方法的调用行为,比如利用 assert_called_once_with 检查方法被正确调用。
  4. 使用 unittest.mock:模拟外部依赖的行为。隔离单元测试,确保它们独立于外部系统(如网络、数据库)。验证调用次数和参数,确保函数的行为符合预期。
  5. 使用 @patch:简化 Mock 对象的创建和管理,提供更清晰、更易读的测试代码。

Tips

  1. 独立性:每个单元测试应独立运行,确保不会互相影响。这有助于更容易发现问题来源,便于调试。
  2. 小范围测试:每个测试应专注于一项功能,保持测试的精细度。避免在同一方法中进行多个断言,保持测试的明确性。
  3. 清晰的命名和编码:测试方法应有意义的命名,指明测试内容和预期结果,以便描述他们的测试内容和预期行为。确保测试代码易于理解,结构清晰,注释明了。
  4. 模拟外部依赖:使用 @patchMagicMock 来隔离测试,保持测试专注于单元逻辑,不让外部因素(如数据库、网络请求等)影响结果。保障单测的可重复性,提高稳定性和速度。
  5. 全面覆盖:尽量编写覆盖各种可能情况的测试案例,包括边界条件和异常情况。以确保代码在各种情况下表现正确。确保代码健壮性。
  6. 持续集成:将单元测试集成到持续集成(CI)工作流中,确保每次代码变更后都能自动测试,避免引入新的缺陷。
  7. 及时更新:保持测试代码与生产代码同步更新,以避免测试数据的陈旧和不一致。保持测试代码清晰,易于理解和维护。
  8. 覆盖率工具:使用代码覆盖率工具(如 coverage.py)来确保测试覆盖了代码的各个部分,但也要注意覆盖率不是唯一的质量指标。

通过遵循这些最佳实践,可以确保单元测试的质量,提高代码的可靠性和可维护性。

结语

单元测试是确保代码质量和可靠性的关键手段之一。通过对代码的最小单元进行独立测试,开发者可以更早地发现和修复缺陷,同时在进行代码重构和变更时保持稳妥。Mock 测试可以有效地隔离外部依赖,使测试更加独立和可重复,但需要谨慎使用,以避免过度 Mock 导致的测试与实际场景脱节。从实际开发经验中,逐步积累完善单元测试技术和最佳实践,能显著提升开发效率和代码质量。


  • 上一篇:时间戳:以不变应万变
  • 专栏:「计算通践」

相关文章:

单元测试:为工程质量保驾护航

单元测试 单元测试是软件开发过程中确保代码质量和正确性的关键手段。它指的是对软件中的最小可测试单元(通常是函数或方法)进行验证,确保其行为符合预期。 基本概念 单元测试:验证软件中最小单元(通常是函数或方法…...

江协科技STM32学习笔记

第01章 STM32简介及开发环境搭建 1.1 STM32简介 1.1.1 STM32F103C8T6 系列:主流系列STM32F1 内核:ARM Cortex-M3 主频:72MHz RAM:20K(SRAM) ROM:64K(Flash) 供电…...

RabbitMQ再回首--往事如梦

这文章你就读吧,越读越🥸,一读一个不吱声 可靠的🐰警官:rabbitMQ,功能全面,不丢数据,体量小,容易堆积 声明exchange channel . exchangeDeclare ( String exchange , …...

头狼择校小程序

综述介绍 头狼择校,是头狼择™高校的简称,我们专注高校、大学的择校。倡导先嗅就业再择校,是预约工具和对话平台。帮您嗅招办、嗅教授、嗅学姐,预约择校有关的老师、顾问,助力考大学和考研的“双考”学生及家长了解就…...

【Electron】npm安装Electron项目失败报错问题和解决办法

前言 闲来无事,便想着研究一下Electron,没想到安装直接就卡住了 问题 npm ERR! RequestError: Hostname/IP does not match certificates altnames: Host: npm.taobao.org. is not in the certs altnames: DNS:*.tbcdn.cn, DNS:*.taobao.com, DNS:*.al…...

人工智能提示(prompt)工程入门

文章目录 人工智能提示(prompt)工程入门一、目的二、使用1、角色2、提示3、上下文4、例子5、输入6、输出 三、使用示例 人工智能提示(prompt)工程入门 一、目的 对于当前的发达的人工智能,我们可以广泛使用&#xff0…...

【机器学习的基本思想】模型优化与评估

【作者主页】Francek Chen 【专栏介绍】 ⌈ ⌈ ⌈Python机器学习 ⌋ ⌋ ⌋ 机器学习是一门人工智能的分支学科,通过算法和模型让计算机从数据中学习,进行模型训练和优化,做出预测、分类和决策支持。Python成为机器学习的首选语言,…...

公司电脑监控软件推荐(一口气了解8款!)一起领略电脑监控界的刀光剑影!

企业的内部管理的需求日益复杂,电脑监控软件作为提升工作效率、保障数据安全的重要工具,其重要性不言而喻。今天,我们将带您一口气了解8款顶尖的公司电脑监控软件,包括国内知名的“安企神”以及多款来自海外的优秀产品&#xff0c…...

设备图纸资料管理系统:数字化转型下的高效协同与安全管理新篇章

在当今高度信息化的时代,设备图纸资料管理系统作为企业资产与知识管理的重要一环,正日益凸显其不可或缺的价值。这一系统集成了先进的数字化技术与管理理念,旨在实现设备图纸资料的高效存储、快速检索、安全共享及版本控制,为企业…...

ArcGIS基础:标注转注记及简单处理

注记是一个静态的标签图层,能够独立的保存为文件,并且具有计算功能; 标注是一个动态的标签图形,无法以文件的形式进行存储和计算; 2者各有优势和劣势,根据具体需求进行选择 需要注意的是注记要存储在GDB…...

jQuery实现图片轮播效果

实现图片轮播效果,打开页面,每隔3秒切换至下一张图片;光标移入数字时,播放相应图片。 思路: (1)获取需要轮播的图片和展示的div。 (2)使用animate设置left值&#xff…...

关于天地图新手使用

1分钟带你了解学习天地图 适用新手 天地图API (tianditu.gov.cn) 文档api 先去注册key 把脚本放到index.html文件里面 <!-- 天地图的官网申请的tk --> <script src="http://api.tianditu.gov.cn/api?v=4.0&tk=申请的key" type="text/javascr…...

STM32与Arduino和ESP32对比分析

在嵌入式系统领域&#xff0c;STM32、Arduino 和 ESP32 是三种广泛使用的微控制器平台。它们各自具有独特的优势&#xff0c;适用于不同类型的项目。本文将详细比较这些平台&#xff0c;帮助您了解它们之间的差异、优势以及可能的应用场景。 一、架构与处理能力 STM32&#xf…...

125. 验证回文串【 力扣(LeetCode) 】

一、题目描述 如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后&#xff0c;短语正着读和反着读都一样。则可以认为该短语是一个 回文串 。 字母和数字都属于字母数字字符。 给你一个字符串 s&#xff0c;如果它是 回文串 &#xff0c;返回 true &#xff1b…...

3年经验,面试测试岗20k都拿不到了吗?

我的情况 大概介绍一下个人情况&#xff0c;女&#xff0c;本科&#xff0c;三年多测试工作经验&#xff0c;懂python&#xff0c;会写脚本&#xff0c;会selenium&#xff0c;会性能&#xff0c;然而到今天都没有收到一份offer&#xff01;从年后就开始准备简历&#xff0c;年…...

【ML】强化学习(Reinforcement Learning)及其拆解

【ML】强化学习&#xff08;Reinforcement Learning&#xff09; 1. RL Outline 强化学习&#xff08;Reinforcement Learning&#xff09;概述1.1 RL的基本框架 2. RL 引入&#xff1a;从这个小游戏开始3. Policy Gradient 方法4. Actor-Critic 方法5. [奖励塑形&#xff08;R…...

在宝塔面板下安装WordPress

宝塔面板是服务器管理好助手&#xff0c;尤其在Linux系统下&#xff0c;提高了管理的可视化&#xff0c;降低了Linux服务器的使用门槛。 WordPress是个非常好的博客系统&#xff0c;由于支持海量主题模板、各种类型的插件&#xff0c;因此已经成为建设各类网站的首选框架。 今…...

纷享销客CRM AI产品架构概览、产品特色

一、纷享销客CRM AI产品架构概览 纷享AI平台架构分为三个主要层次&#xff1a;AI基础设施层、AI平台层和AI应用层。每个层次都由一系列功能模块组成&#xff0c;旨在为客户提供强大的技术支持和灵活的解决方案。 1.Al基础设施层 AI基础设施层是整个AI平台的底层支撑&#xff…...

【文件IO】文件系统操作

文章目录 基本操作概述1. 文件属性2. 文件构造方法3. 文件方法1. 文件创建2. 文件删除3. 查看目录下所有的文件名4. 遍历目录5. 创建目录5. 目录重命名 基本操作概述 创建文件删除文件创建目录重命名文件判定文件存在… Java 中&#xff0c;提供了一个 File 类&#xff0c;进…...

Spring Cloud Alibaba 集成分布式定时任务调度功能

作者&#xff1a;千习 背景简介 定时任务是指在约定的时间&#xff0c;或者按照固定频率周期性执行的任务。在企业应用中&#xff0c;非用户行为发起的后台业务&#xff0c;一般都是通过定时任务来实现&#xff0c;常见场景如下&#xff1a; 异步数据处理&#xff1a;比如先…...

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...

练习(含atoi的模拟实现,自定义类型等练习)

一、结构体大小的计算及位段 &#xff08;结构体大小计算及位段 详解请看&#xff1a;自定义类型&#xff1a;结构体进阶-CSDN博客&#xff09; 1.在32位系统环境&#xff0c;编译选项为4字节对齐&#xff0c;那么sizeof(A)和sizeof(B)是多少&#xff1f; #pragma pack(4)st…...

渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止

<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet&#xff1a; https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...

大语言模型如何处理长文本?常用文本分割技术详解

为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...

BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践

6月5日&#xff0c;2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席&#xff0c;并作《智能体在安全领域的应用实践》主题演讲&#xff0c;分享了在智能体在安全领域的突破性实践。他指出&#xff0c;百度通过将安全能力…...

实现弹窗随键盘上移居中

实现弹窗随键盘上移的核心思路 在Android中&#xff0c;可以通过监听键盘的显示和隐藏事件&#xff0c;动态调整弹窗的位置。关键点在于获取键盘高度&#xff0c;并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...

[Java恶补day16] 238.除自身以外数组的乘积

给你一个整数数组 nums&#xff0c;返回 数组 answer &#xff0c;其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法&#xff0c;且在 O(n) 时间复杂度…...

JavaScript 数据类型详解

JavaScript 数据类型详解 JavaScript 数据类型分为 原始类型&#xff08;Primitive&#xff09; 和 对象类型&#xff08;Object&#xff09; 两大类&#xff0c;共 8 种&#xff08;ES11&#xff09;&#xff1a; 一、原始类型&#xff08;7种&#xff09; 1. undefined 定…...

PostgreSQL——环境搭建

一、Linux # 安装 PostgreSQL 15 仓库 sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-$(rpm -E %{rhel})-x86_64/pgdg-redhat-repo-latest.noarch.rpm# 安装之前先确认是否已经存在PostgreSQL rpm -qa | grep postgres# 如果存在&#xff0…...

Rust 开发环境搭建

环境搭建 1、开发工具RustRover 或者vs code 2、Cygwin64 安装 https://cygwin.com/install.html 在工具终端执行&#xff1a; rustup toolchain install stable-x86_64-pc-windows-gnu rustup default stable-x86_64-pc-windows-gnu ​ 2、Hello World fn main() { println…...