Spring Boot 接口与单元测试

一、引言

在现代软件开发中,构建高质量的应用是至关重要的目标。Spring Boot 作为一种流行的 Java 开发框架,为快速构建企业级应用提供了强大的支持。然而,仅仅依靠开发过程中的调试是远远不够的,单元测试作为一种有效的质量保障手段,能够在开发阶段及时发现问题,降低后期维护成本。本文将围绕 Spring Boot 接口的单元测试展开,详细介绍如何进行有效的单元测试,为构建高质量的 Spring Boot 应用奠定基础。

二、单元测试的重要性

(一)提高代码质量

  1. 早期发现问题
    • 单元测试能够在开发过程的早期发现代码中的问题,避免问题在后期集成测试或生产环境中才暴露出来,从而减少修复问题的成本。
    • 例如,在编写接口方法时,如果没有进行单元测试,可能会在后续的系统集成中才发现接口的逻辑错误,这时修复问题可能需要涉及多个模块的修改,增加了开发的难度和时间成本。
  2. 增强代码可维护性
    • 良好的单元测试可以作为代码的一种文档形式,帮助其他开发人员理解代码的功能和行为。
    • 当代码需要修改时,单元测试可以快速验证修改是否影响了其他部分的功能,提高代码的可维护性。
    • 例如,如果一个接口的实现发生了变化,通过运行相关的单元测试可以立即发现变化是否导致了其他部分的功能失效,从而及时进行调整。

(二)加速开发过程

  1. 提供快速反馈
    • 单元测试可以在短时间内运行,为开发人员提供快速的反馈。开发人员可以及时了解代码的正确性,从而更快地进行迭代开发。
    • 相比集成测试或系统测试,单元测试的运行速度通常要快得多,可以在几秒钟或几分钟内完成,而集成测试可能需要数小时甚至更长时间。
  2. 支持重构
    • 在进行代码重构时,单元测试可以确保代码的功能没有被破坏。开发人员可以放心地对代码进行优化和改进,而不用担心引入新的错误。
    • 例如,当对一个复杂的接口方法进行重构时,单元测试可以验证重构后的代码是否仍然满足预期的功能要求,从而保证代码的稳定性。

(三)提升软件可靠性

  1. 减少回归错误
    • 随着软件的不断发展和变化,新的功能可能会引入新的错误,同时也可能影响到已有的功能。单元测试可以帮助检测这些回归错误,确保软件的稳定性。
    • 例如,在添加新的接口或修改现有接口时,单元测试可以验证整个系统的功能是否仍然正常,避免出现新的问题影响到已有的业务逻辑。
  2. 增强信心
    • 有了全面的单元测试覆盖,开发人员和团队对软件的质量会更有信心。在发布软件时,可以更加放心地将其部署到生产环境中。
    • 例如,当一个项目有高覆盖率的单元测试时,开发团队可以更有信心地进行软件的发布,因为他们知道已经对代码进行了充分的测试,减少了潜在的风险。

三、Spring Boot 单元测试环境搭建

(一)引入测试依赖

  1. Maven 项目
    • 在 Maven 项目的pom.xml文件中,添加 Spring Boot 的测试依赖,如spring-boot-starter-test
    • 示例代码如下:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope>
</dependency>

  • 这个依赖包含了 JUnit、Mockito、AssertJ 等常用的测试框架和工具,为 Spring Boot 应用的单元测试提供了强大的支持。

  1. Gradle 项目
    • 在 Gradle 项目的build.gradle文件中,添加 Spring Boot 的测试依赖,如下所示:

testImplementation('org.springframework.boot:spring-boot-starter-test')

  • 同样,这个依赖会为 Gradle 项目的单元测试提供必要的工具和框架。

(二)创建测试类

  1. 测试类的结构
    • 在 Spring Boot 项目中,通常将测试类放在与被测试类相同的包结构下,但在test目录中。
    • 例如,如果被测试的接口类位于com.example.myapp.service包中,那么测试类可以放在com.example.myapp.service.test包中。
    • 测试类的命名通常以被测试类的名称加上Test后缀,例如,如果被测试类是UserService,那么测试类可以命名为UserServiceTest
  2. 注解的使用
    • 在测试类上,通常使用@RunWith(SpringRunner.class)注解来告诉 JUnit 使用 Spring 的测试运行器。
    • 同时,使用@SpringBootTest注解来启动 Spring Boot 的测试环境,这个注解会加载应用的上下文,使得在测试中可以使用 Spring 的依赖注入等功能。
    • 示例代码如下:
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;@RunWith(SpringRunner.class)
@SpringBootTest
public class UserServiceTest {// 测试方法将在这里编写
}

四、Spring Boot 接口单元测试方法

(一)使用 JUnit 进行测试

  1. 基本测试结构
    • JUnit 是一个广泛使用的 Java 单元测试框架,它提供了丰富的注解和断言方法,方便进行单元测试的编写。
    • 在测试方法上,使用@Test注解来标识一个测试方法。测试方法通常是一个独立的方法,用于测试被测试类的某个特定功能。
    • 示例代码如下:
import org.junit.jupiter.api.Test;public class MyServiceTest {@Testpublic void testMyMethod() {// 测试逻辑在这里编写}
}

  1. 断言的使用
    • JUnit 提供了多种断言方法,用于验证测试结果是否符合预期。例如,可以使用assertEquals方法来验证两个值是否相等,使用assertTrue方法来验证一个条件是否为真,使用assertFalse方法来验证一个条件是否为假等。
    • 示例代码如下:

import org.junit.jupiter.api.Test;public class MyServiceTest {@Testpublic void testMyMethod() {int result = myService.myMethod(5);assertEquals(10, result);}
}

  • 在这个例子中,测试方法调用了被测试类的myMethod方法,并传入参数 5,然后使用assertEquals断言方法来验证返回值是否为 10。

(二)使用 Mockito 进行模拟测试

  1. 模拟对象的创建
    • Mockito 是一个流行的 Java 模拟框架,它可以用于创建模拟对象,以便在单元测试中模拟外部依赖的行为。
    • 使用@Mock注解可以创建一个模拟对象。例如,如果有一个接口MyDependency,可以在测试类中使用@Mock注解创建一个模拟对象,如下所示:

import org.mockito.Mock;public class MyServiceTest {@Mockprivate MyDependency myDependency;
}

  1. 模拟对象的行为设置
    • 创建模拟对象后,可以使用 Mockito 的方法来设置模拟对象的行为。例如,可以使用when方法来设置模拟对象的方法调用返回值。
    • 示例代码如下:

import org.junit.jupiter.api.Test;
import org.mockito.Mockito;public class MyServiceTest {@Mockprivate MyDependency myDependency;@Testpublic void testMyMethod() {// 设置模拟对象的行为Mockito.when(myDependency.someMethod(5)).thenReturn(10);MyService myService = new MyService(myDependency);int result = myService.myMethod(5);assertEquals(10, result);}
}

  • 在这个例子中,测试方法设置了模拟对象myDependencysomeMethod方法在传入参数 5 时返回值为 10。然后,创建了被测试类MyService的实例,并传入模拟对象。最后,调用被测试类的myMethod方法,并验证返回值是否为 10。

(三)结合 Spring 的依赖注入进行测试

  1. 自动注入被测试对象
    • 在 Spring Boot 的单元测试中,可以使用 Spring 的依赖注入功能,自动注入被测试对象。这样可以避免在测试代码中手动创建被测试对象,提高测试代码的可维护性。
    • 示例代码如下:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;public class MyServiceTest {@Autowiredprivate MyService myService;@Testpublic void testMyMethod() {int result = myService.myMethod(5);assertEquals(10, result);}
}

  • 在这个例子中,使用@Autowired注解自动注入了被测试类MyService的实例。然后,在测试方法中调用被测试类的方法,并进行断言验证。

  1. 注入模拟对象
    • 同样,可以使用@Autowired注解注入模拟对象,以便在测试中模拟外部依赖的行为。
    • 示例代码如下:

import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.springframework.beans.factory.annotation.Autowired;public class MyServiceTest {@Mockprivate MyDependency myDependency;@Autowiredprivate MyService myService;@Testpublic void testMyMethod() {// 设置模拟对象的行为Mockito.when(myDependency.someMethod(5)).thenReturn(10);int result = myService.myMethod(5);assertEquals(10, result);}
}

  • 在这个例子中,同时使用了@Mock注解创建模拟对象和@Autowired注解注入被测试对象和模拟对象。在测试方法中,设置了模拟对象的行为,并调用被测试类的方法进行测试。

五、实际示例分析

(一)一个简单的 Spring Boot 接口

  1. 接口定义
    • 假设我们有一个简单的 Spring Boot 应用,其中包含一个用户服务接口UserService,用于处理用户相关的业务逻辑。
    • 接口定义如下:

package com.example.myapp.service;public interface UserService {User findUserById(Long id);
}

  • 这个接口有一个方法findUserById,用于根据用户 ID 查找用户。

  1. 接口实现
    • 接口的实现类UserServiceImpl如下:

package com.example.myapp.service.impl;import com.example.myapp.service.UserService;
import org.springframework.stereotype.Service;@Service
public class UserServiceImpl implements UserService {@Overridepublic User findUserById(Long id) {// 这里可以是实际的数据库查询逻辑或其他业务逻辑return new User(id, "John Doe");}
}

  • 在这个实现类中,目前只是简单地返回一个硬编码的用户对象,实际应用中可以是从数据库或其他数据源获取用户信息。

(二)为接口编写单元测试

  1. 测试类的创建
    • UserService接口编写单元测试类UserServiceTest,如下所示:

package com.example.myapp.service.test;import com.example.myapp.service.UserService;
import com.example.myapp.service.impl.UserServiceImpl;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.when;public class UserServiceTest {@Mockprivate UserService userService;@BeforeEachpublic void setUp() {MockitoAnnotations.initMocks(this);}@Testpublic void testFindUserById() {Long userId = 1L;when(userService.findUserById(userId)).thenReturn(new User(userId, "Test User"));User user = userService.findUserById(userId);assertEquals(userId, user.getId());assertEquals("Test User", user.getName());}
}

  • 在这个测试类中,使用@Mock注解创建了一个模拟的UserService对象。在setUp方法中,使用MockitoAnnotations.initMocks(this)初始化模拟对象。在测试方法testFindUserById中,设置了模拟对象的行为,即当调用findUserById方法传入参数 1L 时,返回一个特定的用户对象。然后,调用模拟对象的方法,并进行断言验证。

  1. 结合实际数据库查询的测试
    • 如果要结合实际的数据库查询进行测试,可以使用 Spring Boot 的测试框架提供的功能来模拟数据库环境。
    • 首先,在测试类中添加对数据库相关组件的自动注入,如下所示:
import com.example.myapp.service.UserService;
import com.example.myapp.service.impl.UserServiceImpl;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.jdbc.core.JdbcTemplate;import static org.junit.jupiter.api.Assertions.assertEquals;@SpringBootTest
public class UserServiceTest {@Autowiredprivate UserService userService;@Autowiredprivate JdbcTemplate jdbcTemplate;@BeforeEachpublic void setUp() {// 可以在 setUp 方法中进行数据库初始化操作,例如插入测试数据jdbcTemplate.execute("INSERT INTO users (id, name) VALUES (1, 'Test User')");}@Testpublic void testFindUserById() {Long userId = 1L;User user = userService.findUserById(userId);assertEquals(userId, user.getId());assertEquals("Test User", user.getName());}
}

  • 在这个例子中,使用@SpringBootTest注解启动了 Spring Boot 的测试环境,并自动注入了UserServiceJdbcTemplate对象。在setUp方法中,使用JdbcTemplate执行 SQL 语句插入了一条测试数据。然后,在测试方法中调用userServicefindUserById方法,并进行断言验证。

六、单元测试的最佳实践

(一)独立性原则

  1. 每个测试只测试一个功能
    • 单元测试应该尽可能地独立,每个测试只测试被测试类的一个特定功能。这样可以确保测试的准确性和可维护性。
    • 例如,如果一个方法有多个功能点,应该为每个功能点编写一个独立的测试方法,而不是在一个测试方法中测试多个功能点。
  2. 避免依赖外部资源
    • 单元测试应该尽量避免依赖外部资源,如数据库、文件系统、网络等。如果必须依赖外部资源,可以使用模拟对象或测试替身来模拟外部资源的行为。
    • 例如,如果一个方法需要从数据库中读取数据,可以使用模拟的数据库连接或数据库查询结果来进行测试,而不是直接连接真实的数据库。

(二)可读性原则

  1. 清晰的测试命名
    • 测试方法的命名应该清晰地表达测试的目的和功能。使用描述性的命名可以提高测试的可读性和可维护性。
    • 例如,一个测试方法用于验证用户服务的findUserById方法,可以命名为testFindUserByIdReturnsCorrectUser
  2. 简洁的测试逻辑
    • 测试方法的逻辑应该简洁明了,易于理解。避免使用复杂的逻辑和过多的嵌套结构。
    • 例如,如果一个测试方法需要进行多个断言验证,可以将每个断言验证放在一个独立的语句中,而不是使用复杂的条件表达式进行多个断言验证。

(三)可维护性原则

  1. 及时更新测试
    • 当代码发生变化时,相应的单元测试也应该及时更新。确保单元测试始终与代码保持同步,能够准确地反映代码的功能和行为。
    • 例如,如果一个方法的实现发生了变化,应该检查相关的单元测试是否仍然有效,并根据需要进行更新。
  2. 避免重复代码
    • 在单元测试中,应该避免重复的代码。可以使用测试框架提供的功能,如测试套件、参数化测试等,来减少重复的测试代码。
    • 例如,如果有多个测试方法需要进行相同的初始化操作,可以将这些初始化操作放在一个@BeforeEach方法中,而不是在每个测试方法中重复编写初始化代码。

七、常见问题及解决方案

(一)测试失败但代码正确

  1. 原因分析
    • 这种情况可能是由于测试的预期结果设置不正确,或者测试的逻辑存在错误。
    • 例如,可能是断言的条件设置得过于严格,或者测试方法中对被测试方法的调用参数不正确。
  2. 解决方案
    • 仔细检查测试方法中的断言条件,确保预期结果与实际结果的比较是正确的。
    • 检查测试方法中对被测试方法的调用参数是否正确,是否与被测试方法的实际参数要求一致。
    • 可以使用调试工具来跟踪测试的执行过程,查看在测试过程中变量的值是否符合预期。
    • 如果可能,可以逐步简化测试逻辑,以确定问题所在。例如,可以先去掉一些断言或测试步骤,逐步增加,以确定是哪个部分导致了测试失败。

(二)测试运行缓慢

  1. 原因分析
    • 可能是测试中涉及了大量的外部资源访问,如数据库查询、网络请求等。
    • 也可能是测试的逻辑过于复杂,导致执行时间过长。
    • 另外,如果测试环境的配置不当,也可能影响测试的运行速度。
  2. 解决方案
    • 尽量避免在单元测试中访问外部资源。如果必须访问,可以使用模拟对象或测试替身来代替真实的外部资源,以减少测试的执行时间。
    • 优化测试的逻辑,避免不必要的复杂计算和循环。可以考虑将一些复杂的逻辑提取到单独的方法中,以便在测试中更容易理解和调试。
    • 检查测试环境的配置,确保测试环境的性能良好。可以调整测试框架的配置参数,如线程数、缓存大小等,以提高测试的运行速度。

(三)测试覆盖率不高

  1. 原因分析
    • 可能是测试用例没有覆盖到所有的代码路径,或者没有对一些边界情况进行测试。
    • 也可能是测试的重点不明确,导致一些重要的功能没有得到充分的测试。
  2. 解决方案
    • 分析代码的结构和功能,确定需要测试的重点和难点。针对这些重点和难点,编写更多的测试用例,以确保代码的正确性和稳定性。
    • 使用代码覆盖率工具来分析测试的覆盖率,找出没有被测试覆盖到的代码部分,并针对性地编写测试用例。
    • 可以采用不同的测试方法和策略,如边界值测试、等价类划分等,以提高测试的覆盖率。

八、总结

在 Spring Boot 应用开发中,为接口编写单元测试是保证应用质量的重要环节。通过单元测试,可以在开发过程中及时发现问题,提高代码的可维护性和可靠性。本文详细介绍了 Spring Boot 单元测试的环境搭建、测试方法的选择与运用,以及实际示例分析和最佳实践。同时,也针对常见问题提出了相应的解决方案。希望本文能够帮助 Java 技术专家和架构师更好地理解和应用单元测试,为构建高质量的 Spring Boot 应用提供有力的支持。

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

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

相关文章

【前端】-音乐播放器(源代码和结构讲解,大家可以将自己喜欢的歌曲添加到数据当中,js实现页面动态显示音乐)

前言&#xff1a;音乐播放器是前端开发中的一个经典项目&#xff0c;通过它可以掌握很多核心技术&#xff0c;如音频处理、DOM操作、事件监听、动画效果等。这个项目不仅能提升前端开发的技能&#xff0c;还能让开发者深入理解JavaScript与HTML的协同作用。 页面展示&#xff1…

虚拟机linux7.9下安装mysql

1.MySQL官网下载安装包&#xff1a; MySQL :: Download MySQL Community Server https://cdn.mysql.com/archives/mysql-5.7/mysql-5.7.39-linux-glibc2.12-x86_64.tar.gz 2.解压文件&#xff1a; #tar xvzf mysql-5.7.39-linux-glibc2.12-x86_64.tar.gz 3.移动文件&#…

03_CC2530基于定时器3的Delay_ms函数

CC2530定时器3与Delay_ms延时函数 前言 ​ Delay函数是开发中常用到的函数&#xff0c;可以用于按键消抖&#xff0c;LED闪烁&#xff0c;生成一定频率信号等(软件模拟通讯协议)。由于利用循环执行一定次数的空指令实现的延时函数在精度上并不能让人满意&#xff0c;而用定时…

【系统面试篇】其他相关题目——虚拟内存、局部性原理、分页、分块、页面置换算法

目录 一、相关问题 1. 什么是虚拟内存&#xff1f;为什么需要虚拟内存&#xff1f; &#xff08;1&#xff09;内存扩展 &#xff08;2&#xff09;内存隔离 &#xff08;3&#xff09;物理内存管理 &#xff08;4&#xff09;页面交换 &#xff08;5&#xff09;内存映…

43.第二阶段x86游戏实战2-提取游戏里面的lua

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 本次游戏没法给 内容参考于&#xff1a;微尘网络安全 本人写的内容纯属胡编乱造&#xff0c;全都是合成造假&#xff0c;仅仅只是为了娱乐&#xff0c;请不要…

容器内pip安装Apache Airflow的经历:如何重置初始密码

背景 Apache Airflow™https://github.com/apache/airflow 是一个开源平台&#xff0c;用于开发、调度和监控面向批处理的工作流程。Airflow 可扩展的 Python 框架使您能够构建几乎可以连接任何技术的工作流程。Web 界面有助于管理工作流程的状态。Airflow 可以通过多种方式部…

Java爬虫 爬取某招聘网站招聘信息

Java爬虫 爬取某招聘网站招聘信息 一、系统介绍二、功能展示1.需求爬取的网站内容2.实现流程2.1数据采集2.2页面解析2.3数据存储 三、其它1.其他系统实现 一、系统介绍 系统主要功能&#xff1a;本项目爬取的XX招聘网站 二、功能展示 1.需求爬取的网站内容 2.实现流程 爬虫…

stm32不小心把SWD和JTAG都给关了,程序下载不进去,怎么办?

因为想用STM32F103的PA15引脚&#xff0c;调试程序的时候不小心把SWD和JTAD接口都给关了&#xff0c;先看下罪魁祸首 GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);//关掉JTAG&#xff0c;不关SWGPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable, ENABLE);//关掉SW&am…

雷军-2022.8小米创业思考-11-新零售:用电商思维做新零售,极致的效率+极致的体验。也有弯路,重回极致效率的轨道上。

第十一章 新零售 当我们说到小米模式的时候&#xff0c;其实我们说的是两件东西&#xff1a; 一是小米模式的本质&#xff0c;即高效率的商业模式&#xff1b; 另一件是小米这家公司具象的商业模式&#xff0c;这是小米在实践中摸索、建立的一整套业务模型。 从2015年到202…

C语言实现数据结构之堆

文章目录 堆一. 树概念及结构1. 树的概念2. 树的相关概念3. 树的表示4. 树在实际中的运用&#xff08;表示文件系统的目录树结构&#xff09; 二. 二叉树概念及结构1. 概念2. 特殊的二叉树3. 二叉树的性质4. 二叉树的存储结构 三. 二叉树的顺序结构及实现1. 二叉树的顺序结构2.…

知识课堂之域名系统中实现动态代理

怎么在域名系统中解析动态ip&#xff0c;这一直是一个需要解决的问题&#xff0c;人们对与网络的稳定连接与灵活运用已经成为生活和工作中不可或缺的一部分&#xff0c;因此这样的问题的解决迫在眉睫。 大家对于动态ip是什么&#xff0c;应该都有所了解了&#xff0c;所谓的动…

5G周边知识笔记

这里写目录标题 3GPP 5G标准路径图5G协议规范5G新空口关键指标4G LTE和5G NR新空口技术对比5G新频段FR1FR2 信道带宽上下行解耦新频点规划&#xff0c;信道栅格FR1各频段实际信道栅格和NR-ARFCN范围定义 同步栅格大规模天线阵列新型调制编码技术大规模载波聚合设备到设备直接通…

uniapp配置h5路由模式为history时404

为了不让URL中出现#&#xff0c;让uniapp项目配置h5路由模式为hisory 然而本地好好的&#xff0c;放到服务器上却404了。 解决方法是给nginx配置一个伪静态&#xff1a; location /xxx-html/ {alias /home/nginx_web/xxx_new_html/;try_files $uri $uri/ /xxx-html/index.ht…

python画图|灵活的subplot_mosaic()函数-初逢

【1】引言 前述学习进程中&#xff0c;对hist()函数画直方图已经有一定的探索。然而学无止境&#xff0c;在继续学习的进程中&#xff0c;我发现了一个显得函数subplot_mosaic()&#xff0c;它几乎支持我们随心所欲地排布多个子图。 经过自我探索&#xff0c;我有一些收获&am…

单体架构的 IM 系统设计

先直接抛出业务背景&#xff01; 有一款游戏&#xff0c;日活跃量&#xff08;DAU&#xff09;在两千左右&#xff0c;虽然 DAU 不高&#xff0c;但这两千用户的忠诚度非常高&#xff0c;而且会持续为游戏充值&#xff1b;为了进一步提高用户体验&#xff0c;继续增强用户的忠…

vue实现天地图电子围栏

一、文档 vue3 javascript WGS84、GCj02相互转换 天地图官方文档 注册登录然后申请应用key&#xff0c;通过CDN引入 <script src"http://api.tianditu.gov.cn/api?v4.0&tk您的密钥" type"text/javascript"></script>二、分析 所谓电子围…

【C++前缀和 单调栈】1124. 表现良好的最长时间段|1908

本文涉及的基础知识点 C算法&#xff1a;前缀和、前缀乘积、前缀异或的原理、源码及测试用例 包括课程视频 C单调栈 LeetCode 1124. 表现良好的最长时间段 给你一份工作时间表 hours&#xff0c;上面记录着某一位员工每天的工作小时数。 我们认为当员工一天中的工作小时数大…

qt5将程序打包并使用

一、封装程序 (1)、点击创建项目->库->clibrary &#xff08;2&#xff09;、填写自己想要封装成库的名称&#xff0c;这里我填写的名称为mydll1 &#xff08;3&#xff09;、如果没有特殊的要求&#xff0c;则一路下一步&#xff0c;最终会出现如下文件列表。 (4)、删…

PICO+Unity MR空间锚点

官方链接&#xff1a;空间锚点 | PICO 开发者平台 注意&#xff1a;该功能只能打包成APK在PICO 4 Ultra上真机运行&#xff0c;无法通过串流或PICO developer center在PC上运行。使用之前要开启视频透视。 在 Inspector 窗口中的 PXR_Manager (Script) 面板上&#xff0c;勾选…

网页中的某个元素高度突然无法设置

做网页时本来一个div的高度好好的&#xff0c;结果代码打着打着突然发现有个div的高度变的很小&#xff0c;把我很多在这个div里的元素给搞的看不见了。 找了好久的原因最后发现是这个div的结束标签</div>不小心被我删了,之后把这个</div>给补上就好了。