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

DevOps系列文章 之 Springboot单元测试

在没有代码生成工具或尝试一门新的 ORM框架时,当我们希望不去另外写 Service 和 Controller 来验证 DAO 层的代码不希望只通过接口请求的方式来验证时,这时候单元测试的方式就可以帮助我们满足这一需求。

在我们开发Web应用时,经常会直接去观察结果进行测试。虽然也是一种方式,但是并不严谨。作为开发者编写测试代码来测试自己所写的业务逻辑是,以提高代码的质量、降低错误方法的概率以及进行性能测试等。经常作为开发这写的最多就是单元测试。引入spring-boot-starter-testSpringBoot的测试依赖。该依赖会引入JUnit的测试包,也是我们用的做多的单元测试包。而Spring Boot在此基础上做了很多增强,支持很多方面的测试,例如JPA,MongoDB,Spring MVC(REST)和Redis等。

接下来主要是测试业务逻辑层的代码,REST和Mock测试。

  • 以下测试时均不用另外启动应用
  • springboot-2.7.1默认绑定junit5 而不再是junit4,后期若想使用 junit4 也可以 Add JUnit4 to classpath
  • 若数据库密码配置是密文,解密密钥在启动参数里,先改回明文再测(否则会fail to load ApplicationContext
  • 新版本的单测都有对应的专门注解,它们内置了启动环境容器。不再需要另外写@RunWith(SpringRunner.class) 或@SpringBootTest

注解说明

专门的注解提供方说明
JPA@DataJpaTestorg.springframework.boot.test会默认扫描@Entity和@Repository注解
MyBatis@MybatisTestcom.baomidou.mybatis-spring-boot-starter-test官方提供支持
MyBatis-Plus@MybatisPlusTestcom.baomidou.mybatis-plus-boot-starter-test官方提供支持

其它注解说明:

@AutoConfigureTestDatabase会自动配置一个基于内存的数据库,只要依赖中含有一个可用的基于内存的嵌入式数据库。
结果会自动回滚,不会发生数据库变化。若使用实际数据库可设置 replace值为Replace.NONE
@Rollback是否回滚,通过设置 value 为 true or false 决定测试数据是否写入数据库

1.打印版单测

package com.imooc;import org.junit.jupiter.api.Test;/****/
public class LoggerTest {@Testpublic void test1() {System.out.println("=========");}
}

2

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Logger2Test {// 选择org.slf4j下的接口private final Logger logger = LoggerFactory.getLogger(Logger2Test.class);@Testpublic void test1() {logger.info("info...");}
}

2.测试 JPA

版本: junit5 springboot-2.7.2` 不启动应用 数据库已配

目录:

  1. @Repository注解
  2. @DataJpaTest 注解
  3. @AutoConfigureTestDatabase注解

依赖

spring-boot-starter-web (2.7.2)
spring-boot-starter-test (with junit5)
mysql-connector-java (8.0)
spring-boot-starter-data-jpa
h2  # 构建基于内存的数据库环境

步骤:

  1. @DataJpaTest 注解
  2. @AutoConfigureTestDatabase注解:

最终元素(已验证)

/
@Data
@Entity    // JPA
public class ProductCategory implements Serializable {// ...
}
/
@Repository
public interface ProductCategoryRepository extends JpaRepository<ProductCategory, Integer> {
}
/
@DataJpaTest    // 只启动JPA组件不启动全环境
@AutoConfigureTestDatabase(replace= AutoConfigureTestDatabase.Replace.NONE) // 创建一个基于内存的数据库环境
class ProductCategoryRepositoryTest {@AutowiredProductCategoryRepository repository;@Testpublic void testFindById() {Optional<ProductCategory> byId = repository.findById(1);System.out.println("查询结果:");System.out.println(byId);}
}

控制台

查询结果:
Optional[ProductCategory(categoryId=1, categoryType=1, categoryName=热销榜, createTime=2022-08-03 06:37:48.0, updateTime=2022-08-03 06:37:48.0)]

参考资料:

(方法讲解)https://howtodoinjava.com/spring-boot2/testing/datajpatest-annotation/#1-repository-annotation

(实际代码)https://github.com/lokeshgupta1981/jakarta-projects/tree/master/spring-boot-projects/hibernate-examples

Auto-configured Data JPA Tests

3.测试 Mybatis

版本: junit5 不启动应用 数据库已配 单测 在主模块里

步骤:

  1. @MybatisTest注解
  2. @AutoConfigureTestDatabase注解

依赖(+)

mybatis-spring-boot-starter-test(@MybatisTest)
h2

案例(已验证)

///子模块
@Mapper
public interface LoginMapper {@Select("SELECT * FROM t_user")List<User> selectAllUser();
}
///main模块
@MybatisTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class LoginMapperTest {@AutowiredLoginMapper loginMapper;@Testvoid contextLoads() {List<User> users = loginMapper.selectAllUser();System.out.println("查询所有用户:");System.out.println(users);}
}

日志结果

查询所有用户:
[User(uid=1, uname=123, upwd=password, pwdErr=null), User(uid=2, uname=test, upwd=test, pwdErr=null), User(uid=3, uname=234, upwd=password, pwdErr=null), User(uid=4, uname=234, upwd=password, pwdErr=null), User(uid=5, uname=2345, upwd=password, pwdErr=null), User(uid=6, uname=452345, upwd=password, pwdErr=null), User(uid=7, uname=523, upwd=password, pwdErr=null), User(uid=8, uname=453, upwd=password, pwdErr=null), User(uid=9, uname=234523, upwd=password, pwdErr=null), User(uid=10, uname=45, upwd=password, pwdErr=null), User(uid=11, uname=452345, upwd=password, pwdErr=null), User(uid=12, uname=2345, upwd=password, pwdErr=null)]

参考资料:

mybatis.org@MyBatisTest的使用

4.测试 MyBatis-Plus

过程和 mb 的差不多,它也有另外支持的依赖starter和注解。

引入依赖

com.baomidou.mybatis-plus-boot-starter-test

测试

@MybatisPlusTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@Slf4j
class UserMapperTest {@AutowiredUserMapper userMapper;@Testpublic void testInsert() {User user = User.builder().id(51L).mobile("8808820").password("e10adc3949ba59abbe56e057f20f883e").build();int insert = userMapper.insert(user);log.info("是否插入成功={}", insert == 1);}
}

结果

### Error updating database.  Cause: java.sql.SQLIntegrityConstraintViolationException: Column 'create_time' cannot be null
### The error may exist in com/imooc/sso/mapper/UserMapper.java (best guess)
### The error may involve com.imooc.sso.mapper.UserMapper.insert-Inline
### The error occurred while setting parameters
### SQL: INSERT INTO user  ( id, mobile, password, create_time, update_time )  VALUES  ( ?, ?, ?, ?, ? )

启动成功,说明单测步骤是对头的。

参考资料:Spring boot Mybatis-Plus数据库单测实战(三种方式)_CuteXiaoKe的博客-CSDN博客_mybatisplus单元测试

其它:默认回滚机制

单测默认会自动回滚,如果执行有插入或更新的数据,数据库的数据不会变化。且假若新增/更新数据没设置【非null】字段,单测也一样会通过(可以理解为执行错了随后回滚了?!)

若想写入数据库,禁止回滚,重写@Rollback注解,将value值置为false

@DataJpaTest    // 只启动JPA组件不启动全环境
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) // 创建一个基于内存的数据库环境
@Rollback(value = false)    // 不自动回滚,真实写入数据库
class ProductCategoryRepositoryTest {@AutowiredProductCategoryRepository repository;@Testpublic void testSave() {ProductCategory category = new ProductCategory();System.out.println("插入ing");category.setCategoryId(2);category.setCategoryType(2);category.setCategoryName("女生最爱");category.setCreateTime(new Date());category.setUpdateTime(new Date());repository.save(category);System.out.println("插入一条数据");Optional<ProductCategory> byId = repository.findById(2);System.out.println("插入后查询:" + byId);}
}

控制台

此时真正读写数据库,当插入not null字段时,则会触发ConstraintViolationException(约束违背异常) 否则执行后,此时,数据库数据写入成功。

附:多模块工程的单测

多模块工程:

如果只有一个模板,以上方式都可以;

如果是多模块工程,单测建议放 main 模块,否则会抛异常

`Test ignored.`
java.lang.IllegalStateException: Unable to find a @SpringBootConfiguration, you need to use @ContextConfiguration or @SpringBootTest(classes=...) with your test`·

5.测试Service层

service 层比起 DAO 层要常规得多

测试ServiceImpl

@SpringBootTest
class ProductInfoServiceImplTest {@AutowiredProductInfoService productInfoService;@Testvoid listUpProductInfos() {List<ProductInfo> upProductInfos = productInfoService.listUpProductInfos();System.out.println("验证已上架商品" + upProductInfos);}
}

6.Assertions的使用

Assertions是 junit5 代替 junit4 中的 Assert 的一个静态类,内置一系列的静态方法,提供用户判空,判等等方法验证的 api。跳过打印验证执行结构

assertNotNull(obj)

import org.junit.jupiter.api.Assertions;// 判断返回结果是否为空@Testvoid findOne() {ProductCategory productCategory = productCategoryRepository.findById(3).get();Assertions.assertNotNull(productCategory);}

assertEquals(Expected, Actual)

import org.junit.jupiter.api.Assertions;@Testvoid count() {long count = productCategoryRepository.count();Assertions.assertEquals(count, 10);}

(JUnit5+Mockito)

1. 测试

如果项目组有测试团队,最常接触的概念有:功能测试、回归测试、冒烟测试等,但这些都是由测试人员发起的。

开发人员写的常常是“单元测试”,但其实可以细分成 单元测试 和 集成测试 两个。

划分的原因拿常见的 Spring IoC 举例。Spring 不同Bean之间相互依赖,例如某API业务逻辑中会依赖不同模块的 Service,Service 方法中又可能依赖不同的 Dao 层方法,甚至还会通过 RPC、HTTP 调用外部服务方法。这给我们写测试用例带来了难度,本来只想测试某个方法的功能,却要考虑一连串的依赖关系。

1.1. 单元测试

单元测试:是指对软件中的最小可测试单元进行检查和验证。

通常任何软件都会划分为不同的模块和组件。单独测试一个组件时,我们叫做单元测试。单元测试用于验证相关的一小段代码是否正常工作。单元测试不是用于发现应用程序范围内的 bug,或者回归测试的 bug,而是分别检测每个代码片段。

单元测试不验证应用程序代码是否和外部依赖正常工作。它聚焦与单个组件并且 Mock 所有和它交互的依赖。例如,方法中调用发短信的服务,以及和数据库的交互,我们只需要 Mock 假执行即可,毕竟测试的焦点在当前方法上。

单元测试的特点:

  • 不依赖任何模块。
  • 基于代码的测试,不需要在 ApplicationContext 中运行。
  • 方法执行快,500ms以内(也和不启动 Spring 有关)。
  • 同一单元测试可重复执行N次,并每次运行结果相同。

1.2. 集成测试

集成测试:在单元测试的基础上,将所有模块按照设计要求组装成为子系统或系统,进行集成测试。

集成测试主要用于发现用户端到端请求时不同模块交互产生的问题。集成测试范围可以是整个应用程序,也可以是一个单独的模块,取决于要测试什么。

在集成测试中,我们应该聚焦于从控制器层到持久层的完整请求。应用程序应该运行嵌入服务(例如:Tomcat)以创建应用程序上下文和所有 bean。这些 bean 有的可能会被 Mock 覆盖。

集成测试的特点:

  • 集成测试的目的是测试不同的模块一共工作能否达到预期。
  • 应用程序应该在 ApplicationContext 中运行。Spring boot 提供 @SpringBootTest 注解创建运行上下文。
  • 使用 @TestConfiguration 等配置测试环境。

2. 测试框架

2.1. spring-boot-starter-test

SpringBoot中有关测试的框架,主要来源于 spring-boot-starter-test。一旦依赖了spring-boot-starter-test,下面这些类库将被一同依赖进去:

  • JUnit:java测试事实上的标准。
  • Spring Test & Spring Boot Test:Spring的测试支持。
  • AssertJ:提供了流式的断言方式。
  • Hamcrest:提供了丰富的matcher。
  • Mockito:mock框架,可以按类型创建mock对象,可以根据方法参数指定特定的响应,也支持对于mock调用过程的断言。
  • JSONassert:为JSON提供了断言功能。
  • JsonPath:为JSON提供了XPATH功能。
测试环境自定义Bean
  • @TestComponent:该注解是另一种@Component,在语义上用来指定某个Bean是专门用于测试的。该注解适用于测试代码和正式混合在一起时,不加载被该注解描述的Bean,使用不多。
  • @TestConfiguration:该注解是另一种@TestComponent,它用于补充额外的Bean或覆盖已存在的Bean。在不修改正式代码的前提下,使配置更加灵活。

2.2. JUnit

前者说了,JUnit 是一个Java语言的单元测试框架,但同样可用于集成测试。当前最新版本是 JUnit5。

常见区别有:

  • JUnit4 所需 JDK5+ 版本即可,而 JUnit5 需 JDK8+ 版本,因此支持很多 Lambda 方法。
  • JUnit 4将所有内容捆绑到单个jar文件中。Junit 5由3个子项目组成,即JUnit Platform,JUnit Jupiter和JUnit Vintage。核心是JUnit Jupiter,它具有所有新的junit注释和TestEngine实现,以运行使用这些注释编写的测试。而JUnit Vintage包含对JUnit3、JUnit4的兼容,所以spring-boot-starter-test新版本pom中往往会自动exclusion它。
  • SpringBoot 2.2.0 开始引入 JUnit5 作为单元测试默认库,在 SpringBoot 2.2.0 之前,spring-boot-starter-test 包含了 JUnit4 的依赖,SpringBoot 2.2.0 之后替换成了 Junit Jupiter。

JUnit5 和 JUnit4 在注解上的区别在于:

功能JUnit4JUnit5
声明一种测试方法@Test@Test
在当前类中的所有测试方法之前执行@BeforeClass@BeforeAll
在当前类中的所有测试方法之后执行@AfterClass@AfterAll
在每个测试方法之前执行@Before@BeforeEach
在每个测试方法之后执行@After@AfterEach
禁用测试方法/类@Ignore@Disabled
测试工厂进行动态测试NA@TestFactory
嵌套测试NA@Nested
标记和过滤@Category@Tag
注册自定义扩展NA@ExtendWith
RunWith 和 ExtendWith

在 JUnit4 版本,在测试类加 @SpringBootTest 注解时,同样要加上 @RunWith(SpringRunner.class)才生效,即:

@SpringBootTest
@RunWith(SpringRunner.class)
class HrServiceTest {
...
}

但在 JUnit5 中,官网告知 @RunWith 的功能都被 @ExtendWith 替代,即原 @RunWith(SpringRunner.class) 被同功能的 @ExtendWith(SpringExtension.class) 替代。但 JUnit5 中 @SpringBootTest 注解中已经默认包含了 @ExtendWith(SpringExtension.class)。

因此,在 JUnit5 中只需要单独使用 @SpringBootTest 注解即可。其他需要自定义拓展的再用 @ExtendWith,不要再用 @RunWith 了。

2.3. Mockito

测试驱动的开发(TDD)要求我们先写单元测试,再写实现代码。在写单元测试的过程中,我们往往会遇到要测试的类有很多依赖,这些依赖的类/对象/资源又有别的依赖,从而形成一个大的依赖树。而 Mock 技术的目的和作用是模拟一些在应用中不容易构造或者比较复杂的对象,从而把测试与测试边界以外的对象隔离开。

Mock 框架有很多,除了传统的 EasyMock、Mockito以外,还有PowerMock、JMock、JMockit等。这里选用 Mockito ,是因为 Mockito 在社区流行度较高,而且是 SpringBoot 默认集成的框架。

Mockito 框架中最核心的两个概念就是 Mock 和 Stub。测试时不是真正的操作外部资源,而是通过自定义的代码进行模拟操作。我们可以对任何的依赖进行模拟,从而使测试的行为不需要任何准备工作或者不具备任何副作用。

当我们在测试时,如果只关心某个操作是否执行过,而不关心这个操作的具体行为,这种技术称为 mock。比如我们测试的代码会执行发送邮件的操作,我们对这个操作进行 mock;测试的时候我们只关心是否调用了发送邮件的操作,而不关心邮件是否确实发送出去了。

另一种情况,当我们关心操作的具体行为,或者操作的返回结果的时候,我们通过执行预设的操作来代替目标操作,或者返回预设的结果作为目标操作的返回结果。这种对操作的模拟行为称为 stub(打桩)。比如我们测试代码的异常处理机制是否正常,我们可以对某处代码进行 stub,让它抛出异常。再比如我们测试的代码需要向数据库插入一条数据,我们可以对插入数据的代码进行stub,让它始终返回1,表示数据插入成功。

推荐一个 Mockito 中文文档。

mock 和 spy 的区别

mock方法和spy方法都可以对对象进行mock。但是前者是接管了对象的全部方法,而后者只是将有桩实现(stubbing)的调用进行mock,其余方法仍然是实际调用。

如下例,因为只mock了List.size()方法。如果mockList是通过mock生成的,List的add、get等其他方法都失效,返回的数据都为null。但如果是通过spy生成的,则验证正常。

平时开发过程中,我们通常只需要mock类的某些方法,用spy即可。

@Testvoid mockAndSpy() {List<String> mockList = Mockito.mock(List.class);// List<String> mockList = Mockito.spy(new ArrayList<>());Mockito.when(mockList.size()).thenReturn(100);mockList.add("A");mockList.add("B");Assertions.assertEquals("A", mockList.get(0));Assertions.assertEquals(100, mockList.size());}

2.4. 插件 Squaretest

强烈推荐这个自动生成单元测试代码的插件,像本文说的框架搭配,默认的模板就是 JUnit5Mockito.java.ft

在选中类右键Generate -> Generate Test 后,不光能生成测试类和方法,甚至连Mockito 数据、方法和 Assertions 等都写好了,只需要自己改一改即可。

3. 示例

3.1. 单元测试示例

因为 JUnit5、Mockito 都是 spring-boot-starter-test 默认依赖的,所以 pom 中无需引入其他特殊依赖。先写个简单的 Service 层方法,通过两张表查询数据。
HrService.java

@AllArgsConstructor
@Service
public class HrService {private final OrmDepartmentDao ormDepartmentDao;private final OrmUserDao ormUserDao;List<OrmUserPO> findUserByDeptName(String deptName) {return ormDepartmentDao.findOneByDepartmentName(deptName).map(OrmDepartmentPO::getId).map(ormUserDao::findByDepartmentId).orElse(Collections.emptyList());}
}
IDEA 创建测试类

接下来针对该 Service 类创建测试类,我们使用的开发工具是 IDEA。点进当前类,右键->Go To->Test->Create New Test,在 Testing library 中选择 Junit5,则在对应目录生成测试类和方法。

HrServiceTest.java

@ExtendWith(MockitoExtension.class)
class HrServiceTest {@Mockprivate OrmDepartmentDao ormDepartmentDao;@Mockprivate OrmUserDao ormUserDao;@InjectMocksprivate HrService hrService;@DisplayName("根据部门名称,查询用户")@Testvoid findUserByDeptName() {Long deptId = 100L;String deptName = "行政部";OrmDepartmentPO ormDepartmentPO = new OrmDepartmentPO();ormDepartmentPO.setId(deptId);ormDepartmentPO.setDepartmentName(deptName);OrmUserPO user1 = new OrmUserPO();user1.setId(1L);user1.setUsername("001");user1.setDepartmentId(deptId);OrmUserPO user2 = new OrmUserPO();user2.setId(2L);user2.setUsername("002");user2.setDepartmentId(deptId);List<OrmUserPO> userList = new ArrayList<>();userList.add(user1);userList.add(user2);Mockito.when(ormDepartmentDao.findOneByDepartmentName(deptName)).thenReturn(Optional.ofNullable(ormDepartmentPO).filter(dept -> deptName.equals(dept.getDepartmentName())));Mockito.doReturn(userList.stream().filter(user -> deptId.equals(user.getDepartmentId())).collect(Collectors.toList())).when(ormUserDao).findByDepartmentId(deptId);List<OrmUserPO> result1 = hrService.findUserByDeptName(deptName);List<OrmUserPO> result2 = hrService.findUserByDeptName(deptName + "error");Assertions.assertEquals(userList, result1);Assertions.assertEquals(Collections.emptyList(), result2);}

因为单元测试不用启动 Spring 容器,则无需加 @SpringBootTest,因为要用到 Mockito,只需要自定义拓展 MockitoExtension.class 即可,依赖简单,运行速度更快。

可以明显看到,单元测试写的代码,怎么是被测试代码长度的好几倍?其实单元测试的代码长度比较固定,都是造数据和打桩,但如果针对越复杂逻辑的代码写单元测试,还是越划算的。

3.2. 集成测试示例

还是那个方法,如果使用Spring上下文,真实的调用方法依赖,可直接用下列方式:

@SpringBootTest
class HrServiceTest {@Autowiredprivate HrService hrService;@DisplayName("根据部门名称,查询用户")@Testvoid findUserByDeptName() {List<OrmUserPO> userList = hrService.findUserByDeptName("行政部");Assertions.assertTrue(userList.size() > 0);}  
}

还可以使用@MockBean@SpyBean替换Spring上下文中的对应的Bean:

@SpringBootTest
class HrServiceTest {@Autowiredprivate HrService hrService;@SpyBeanprivate OrmDepartmentDao ormDepartmentDao;@DisplayName("根据部门名称,查询用户")@Testvoid findUserByDeptName() {String deptName="行政部";OrmDepartmentPO ormDepartmentPO = new OrmDepartmentPO();ormDepartmentPO.setDepartmentName(deptName);Mockito.when(ormDepartmentDao.findOneByDepartmentName(ArgumentMatchers.anyString())).thenReturn(Optional.of(ormDepartmentPO));List<OrmUserPO> userList = hrService.findUserByDeptName(deptName);Assertions.assertTrue(userList.size() > 0);}
}
小提示:@SpyBean 和 spring boot data 的问题

当用 @SpyBean 添加到 spring data jpa 的dao层上时(继承 JpaRepository 的接口),会无法启动容器,报错 org.springframework.beans.factory.BeanCreationException: Error creating bean with name。包括 mongo 等 spring data 都会有此问题,是 spring boot 官方不支持,可查看 Issues-7033,已在 spring boot 2.5.3 版本修复。

附:spring1.x junit4 的测试

Repository接口的@Repository注解可写可不写

Test类的必要注解:2个

@RunWith(SpringRunner.class)    // 启动容器环境
@SpringBootTest
class ProductCategoryRepositoryTest {
ProductCategoryRepository repository;@Testpublic void testFindById() {Optional<ProductCategory> byId = repository.findById(1);System.out.println("查询结果:");System.out.println(byId);}
}

其它

  • IDEA中在目标类内部使用快捷键 [ctrl+shift+T] 快速生成测试类
  • 有关注解 AutoConfigureTestDatabase
If you prefer your test to run against a real database, you can use the  @AutoConfigureTestDatabase annotation in the same way as for  DataJpaTest. (See " Auto-configured Data JPA Tests".)
--  Spring Boot Reference Documentation

相关文章:

DevOps系列文章 之 Springboot单元测试

在没有代码生成工具或尝试一门新的 ORM框架时&#xff0c;当我们希望不去另外写 Service 和 Controller 来验证 DAO 层的代码不希望只通过接口请求的方式来验证时&#xff0c;这时候单元测试的方式就可以帮助我们满足这一需求。 在我们开发Web应用时&#xff0c;经常会直接去观…...

04 linux之C 语言高级编程

gcc和gdb GNU工具 编译工具&#xff1a;把一个源程序编译为一个可执行程序调试工具&#xff1a;能对执行程序进行源码或汇编级调试软件工程工具&#xff1a;用于协助多人开发或大型软件项目的管理&#xff0c;如make、CVS、Subvision其他工具&#xff1a;用于把多个目标文件链…...

深入学习 Redis - Stream、Geospatial、HyperLogLog、Bitmap、Bitfields 类型扩展

目录 前言 Stream geospatial HyperLogLog Bitmaps Bitfields 前言 redis 中最关键的五个数据类型 String、List、Hash、Set、Zset 应用最广泛&#xff0c;同时 redis 也推出了额外的 5 个数据类型&#xff0c;他们分别是针对特殊场景才进行的应用的. Ps&#xff1a;这几种…...

Windows11+Opencv+Clion编译源码

Windows11OpencvClion编译源码 参考&#xff1a;https://www.robotsfan.com/posts/69395e08.html 注意事项 编译过程中使用的软件&#xff0c;开源码等所有工具的安装路径一定不要有中文和空格。cmake过程会下载一些文件&#xff0c;如果是局域网的话可能下载不下来&#xf…...

【机器学习】Cost Function

Cost Function 1、计算 cost2、cost 函数的直观理解3、cost 可视化总结附录 首先&#xff0c;导入所需的库&#xff1a; import numpy as np %matplotlib widget import matplotlib.pyplot as plt from lab_utils_uni import plt_intuition, plt_stationary, plt_update_onclic…...

【黑马头条之内容安全第三方接口】

本笔记内容为黑马头条项目的文本-图片内容审核接口部分 目录 一、概述 二、准备工作 三、文本内容审核接口 四、图片审核接口 五、项目集成 一、概述 内容安全是识别服务&#xff0c;支持对图片、视频、文本、语音等对象进行多样化场景检测&#xff0c;有效降低内容违规风…...

回归预测 | MATLAB实现GRNN广义回归神经网络多输入单输出回归预测(多指标,多图)

回归预测 | MATLAB实现GRNN广义回归神经网络多输入单输出回归预测(多指标,多图) 目录 回归预测 | MATLAB实现GRNN广义回归神经网络多输入单输出回归预测(多指标,多图)效果一览基本介绍程序设计参考资料效果一览 基本介绍 MATLAB实现GRNN广义回归神经网络多输入单输出回归…...

STM32 HAL库函数——HAL_UART_RxCpltCallback()详解

HAL_UART_RxCpltCallback函数 他是谁&#xff0c;他和谁有关功能用法每收到一个字符&#xff0c;就自动调用一次&#xff1f;&#xff1f;示例----接收未知长度的字符 他是谁&#xff0c;他和谁有关 HAL_UART_RxCpltCallback 是一个回调函数&#xff0c;用于在使用 HAL 库进行…...

前端调用合约如何避免出现transaction fail

前言&#xff1a; 作为开发&#xff0c;你一定经历过调用合约的时候发现 gas fee 超出限制&#xff0c;但是不知道报了什么错。这个时候一般都是触发了require错误合约校验。对于用户来说他不理解为什么一笔交易会花费如此大的gas&#xff0c;那我们作为开发如何尽量避免这种情…...

选择器的使用

目录 层级选择器属性选择器伪类选择器结构伪类选择器目标伪类选择器 层级选择器 /*子代选择器&#xff1a;选出box下的所有li标签*/.box>li{background-color: aliceblue;}/* 选出box后面的第一个兄弟li标签 */.boxli{background-color: aliceblue;}/* 选出box后面的所有兄…...

软考A计划-系统集成项目管理工程师-项目干系人管理-上

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列点击跳转>蓝桥系列 &#x1f449;关于作者 专注于Android/Unity和各种游…...

F5 LTM 知识点和实验 2-负载均衡基础概念

第二章&#xff1a;负载均衡基础概念 目标&#xff1a; 使用网页和TMSH配置virtual servers&#xff0c;pools&#xff0c;monitors&#xff0c;profiles和persistence等。查看统计信息 基础概念&#xff1a; Node一个IP地址。是创建pool池的基础。可以手工创建也可以自动创…...

安全学习DAY08_算法加密

算法加密 漏洞分析、漏洞勘测、漏洞探针、挖漏洞时要用到的技术知识 存储密码加密-应用对象传输加密编码-发送回显数据传输格式-统一格式代码特性混淆-开发语言 传输数据 – 加密型&编码型 安全测试时&#xff0c;通常会进行数据的修改增加提交测试 数据在传输的时候进行…...

OpenCloudOS 与PolarDB全面适配

近日&#xff0c;OpenCloudOS 开源社区签署阿里巴巴开源 CLA (Contribution License Agreement, 贡献许可协议), 正式与阿里云 PolarDB 开源数据库社区牵手&#xff0c;并展开 OpenCloudOS &#xff08;V8&#xff09;与阿里云开源云原生数据库 PolarDB 分布式版、开源云原生数…...

如何在Linux系统中使用yum命令安装MySQL

1、安装软件 # yum install -y https://repo.mysql.com//mysql80-community-release-el7-8.noarch.rpm # yum -y install mysql-community-server网址来源&#xff1a;https://dev.mysql.com/downloads/repo/yum/ 2、启动软件 # systemctl enable mysqld# systemctl start my…...

在Ail Linux中手动配置IPv6

第一步&#xff0c;登录阿里云服务器控制台&#xff0c;在“概览”页面找到对应实例&#xff0c;然后单击实例ID。 第二步&#xff0c;在“实例详情”页面中的“网络信息”栏目中&#xff0c;可以发现“IPv6 地址”中没有数据&#xff0c;然后单击“专有网络”的专有网络ID。 第…...

TCP如何保证服务的可靠性

TCP如何保证服务的可靠性 确认应答超时重传流量控制滑动窗口机制概述发送窗口和接收窗口的工作原理几种滑动窗口协议1比特滑动窗口协议&#xff08;停等协议&#xff09;后退n协议选择重传协议 采用滑动窗口的问题&#xff08;死锁可能&#xff0c;糊涂窗口综合征&#xff09;死…...

【云原生系列】openstack搭建过程及使用

目录 搭建步骤 准备工作 正式部署OpenStack 安装的过程 安装组件如下 登录页面 进入首页 创建实例步骤 上传镜像 配置网络 服务器配置 dashboard配置 密钥配置免密登录 创建实例 绑定浮动ip 免密登录实例 搭建步骤 准备工作 1.关闭防火墙和网关 systemctl dis…...

无涯教程-jQuery - Menu组件函数

小部件菜单功能可与JqueryUI中的小部件一起使用。一个简单的菜单显示项目列表。 Menu - 语法 $( "#menu" ).menu(); Menu - 示例 以下是显示菜单用法的简单示例- <!doctype html> <html lang"en"><head><meta charset"utf-…...

Django用户登录验证和自定义验证类

一、FBV 用户登录验证 1.1 登录验证并加入 session 用户登录时&#xff0c;使用 authenticate 验证用户名和密码是否正确&#xff0c;正确则返回一个用户对象。 用户名默认的字段名是 username 密码默认的字段名是 password 将已验证的用户添加到当前会话(session)中&#x…...

json-server详解

零、文章目录 json-server详解 1、简介 Json-server 是一个零代码快速搭建本地 RESTful API 的工具。它使用 JSON 文件作为数据源&#xff0c;并提供了一组简单的路由和端点&#xff0c;可以模拟后端服务器的行为。github地址&#xff1a;https://github.com/typicode/json-…...

MacOS Monterey VM Install ESXi to 7 U2

一、MacOS Monterey ISO 准备 1.1 下载macOS Monterey 下载&#x1f517;链接 一定是 ISO 格式的&#xff0c;其他格式不适用&#xff1a; https://www.mediafire.com/file/4fcx0aeoehmbnmp/macOSMontereybyTechrechard.com.iso/file 1.2 将 Monterey ISO 文件上传到数据…...

哈工大计算机网络课程网络安全基本原理详解之:消息完整性与数字签名

哈工大计算机网络课程网络安全基本原理详解之&#xff1a;消息完整性与数字签名 这一小节&#xff0c;我们继续介绍网络完全中的另一个重要内容&#xff0c;就是消息完整性&#xff0c;也为后面的数字签名打下基础。 报文完整性 首先来看一下什么是报文完整性。 报文完整性…...

K8s:K8s 20个常用命令汇总

写在前面 博文内容为节译整理&#xff0c;用于温习理解不足小伙伴帮忙指正 对每个人而言&#xff0c;真正的职责只有一个&#xff1a;找到自我。然后在心中坚守其一生&#xff0c;全心全意&#xff0c;永不停息。所有其它的路都是不完整的&#xff0c;是人的逃避方式&#xff0…...

DHCP防护原理

电脑刚连接到网络 是没有IP地址的 。 通过发送广播到DHCPO服务器。 DHCP服务器响应对应的 IP地址&#xff08;简要过程&#xff09;。 如果有人私自挂接WIFI&#xff0c;相当于DHCP服务器&#xff0c;但这个DHCP服务器是假的&#xff0c;就会引起电脑接入获取家用WIFI的地址&…...

leetcode2434. 使用机器人打印字典序最小的字符串 出栈顺序 贪心+栈

https://leetcode.cn/problems/using-a-robot-to-print-the-lexicographically-smallest-string/ 给你一个字符串 s 和一个机器人&#xff0c;机器人当前有一个空字符串 t 。执行以下操作之一&#xff0c;直到 s 和 t 都变成空字符串。请你返回纸上能写出的字典序最小的…...

【程序设计】一文讲解程序设计目标:高内聚,低耦合

前言 软件设计的目标是高内聚、低耦合。 如果代码是高耦合和低内聚的&#xff0c;就会出现修改一个逻辑&#xff0c;会导致多处代码要修改&#xff0c;可能影响到多个业务链路&#xff0c;这增加了出bug的业务风险&#xff0c;同时增加了测试回归的范围&#xff0c;导致研发成…...

nginx mirror代码分析

实现方式 mirror逻辑的工作阶段&#xff1a; ngx在log phase之后&#xff08;在ngx_http_free_request处调用&#xff09;已完成向client端返回response&#xff0c;在log phase之后完成close connection&#xff08;短链接&#xff09;&#xff0c;在该阶段处理mirror逻辑不…...

Python代理模式介绍、使用

一、Python代理模式介绍 Python代理模式&#xff08;Proxy Pattern&#xff09;是一种结构型设计模式。在代理模式中&#xff0c;代理对象充当了另一个对象的占位符&#xff0c;以控制对该对象的访问。 代理对象和被代理对象实现了相同的接口&#xff0c;因此它们可以互相替代…...

《MySQL45讲》笔记—索引

索引 索引是为了提高数据查询效率&#xff0c;就像书的目录一样。如下图&#xff0c;索引和数据就是位于存储引擎中&#xff1a; 索引常见模型 哈希表 以键值对存储的数据结构。适用于只有等值查询的场景。 有序数组 在等值查询和范围查询场景中性能都特别优秀。但是有…...