从零开始 Spring Boot 44:Test

news2024/12/29 0:49:19

从零开始 Spring Boot 44:Test

spring boot

图源:简书 (jianshu.com)

本篇文章我们讨论如何在 Spring 项目中编写测试用例。

当前使用的是 Spring 6.0,默认集成 JUnit 5。

依赖

Spring Boot 的测试功能需要以下依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

通过工具构建 Spring Boot 项目时该依赖都会自动添加,一般不需要手动添加。

简单测试用例

我们从最简单的测试用例开始。

假设我们的 Spring 项目中有这样一个 bean:

@Component
public class FibonacciUtil {
    public int doFibonacci(int n) {
        if (n <= 0) {
            throw new IllegalArgumentException("n 不能小于等于0");
        }
        if (n <= 2) {
            return 1;
        }
        return doFibonacci(n - 1) + doFibonacci(n - 2);
    }
}

这个 bean 很简单,且没有任何其他依赖。因此我们可以用最简单的方式编写测试用例:

package com.example.test;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
public class TestFibonacciUtil {
    @Test
    void testFibonacci() {
        FibonacciUtil util = new FibonacciUtil();
        int[] fibonacci = new int[]{1, 1, 2, 3, 5, 8, 13, 21};
        for (int i = 0; i < fibonacci.length; i++) {
            Assertions.assertEquals(fibonacci[i], util.doFibonacci(i + 1));
        }
        int[] errorIndex = new int[]{0, -1, -2};
        for (int ei : errorIndex) {
            var exp = Assertions.assertThrows(IllegalArgumentException.class, () -> util.doFibonacci(ei));
            Assertions.assertEquals("n 不能小于等于0", exp.getMessage());
        }
    }
}

这里用 Junit 的@Test注解标记testFibonacci方法是一个测试用例。在测试用例中,使用Assertions.assertXXX方法执行测试。具体使用了两种断言:

  • assertEquals,判断执行结果是否与目标相等。
  • assertThrows,判断执行后是否会产生一个指定类型的异常。

就像示例中的那样,Junit 的断言方法都是Assertions类的静态方法,因此也可以用以下这样的"简写"方式:

// ...
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

public class TestFibonacciUtil {
    @Test
    void testFibonacci() {
        // ...
        assertEquals(fibonacci[i], util.doFibonacci(i + 1));
        // ...
        var exp = assertThrows(IllegalArgumentException.class, () -> util.doFibonacci(ei));
        assertEquals("n 不能小于等于0", exp.getMessage());
    }
}

上下文

但对于存在依赖注入的情况,要想编写测试用例就可能变得复杂,比如有这么一个 Service:

@Service
public class FibonacciService {
    @Autowired
    private FibonacciUtil fibonacciUtil;

    public int fibonacci(int n) {
        return fibonacciUtil.doFibonacci(n);
    }
}

为其编写测试用例:

public class TestFibonacciService {
    private static FibonacciService fibonacciService = new FibonacciService();

    @SneakyThrows
    @BeforeAll
    public static void init() {
        var cls = FibonacciService.class.getDeclaredField("fibonacciUtil");
        cls.setAccessible(true);
        cls.set(fibonacciService, new FibonacciUtil());
    }

    @Test
    public void testFibonacci() {
        int[] fibonacciArr = new int[]{1, 1, 2, 3, 5, 8};
        for (int i = 0; i < fibonacciArr.length; i++) {
            Assertions.assertEquals(fibonacciArr[i], fibonacciService.fibonacci(i + 1));
        }
        int[] errorIndexes = new int[]{0, -1, -2};
        for (int ei : errorIndexes) {
            Assertions.assertThrows(IllegalArgumentException.class, () -> fibonacciService.fibonacci(ei));
        }
    }
}

为了能够处理依赖关系,这里不得不通过反射添加了FibonacciServicefibonacciUtil属性。

如果是通过构造器注入或者 Setter 注入,这里的处理会简单很多。

如果能够为我们的测试用例生成所需的上下文(Context),那岂不是可以使用“自动连接”来完成注入?

@ContextConfiguration

实际上的确如此,通过@ContextConfiguration我们可以完成类似的目的:

@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = {FibonacciUtil.class, FibonacciService.class})
public class TestFibonacciService4 {
    @Autowired
    private FibonacciService fibonacciService;
	// ...
}

这里为@ContextConfigurationclasses属性添加了两个组件类型:FibonacciUtil.classFibonacciService.class,利用这两个组件类型就可以完成对FibonacciService的“自动连接”。

所谓的“组件类型”就是作为 bean 定义的类型,包括@Configuration@Component@Service等标记的类(用@SpringBootApplication标记的入口类同样属于,因为@SpringBootApplication包含了@Configuration注解)。

此外,这里的@ExtendWith注解是 Junit5 提供的用来扩展 Junit 功能的注解,而SpringExtension类正是 Spring 扩展 Junit 的类,它通过覆盖 Junit 的相关接口(比如beforeAll)扩展了相应的功能。换言之,用@ExtendWith(SpringExtension.class)标记的测试类,其中的@BeforeAll等 Junit 相关注解在执行时将执行 Spring 扩展后的相关代码。

如果使用的是 JUnit4,需要用@RunWith(SpringRunner.class)来集成 Spring 扩展。

当然,我们指定 Spring Boot 的入口类来导入所有的组件类别(组件的自动扫描功能),而不是具体的某几个:

@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = {TestApplication.class})
public class TestFibonacciService5 {
    @Autowired
    private FibonacciService fibonacciService;
    // ...
}

除了通过classes属性指定组件类,还可以通过locations属性指定 XML 配置文件来加载上下文。如果既没有指定classes,也没有指定locations,Spring 会查找测试类中的内嵌配置类来生成上下文:

@ExtendWith(SpringExtension.class)
@ContextConfiguration
public class TestFibonacciService6 {
    @Configuration
    public static class Config {
        @SneakyThrows
        @Bean
        FibonacciService fibonacciService() {
            FibonacciService fibonacciService = new FibonacciService();
            return fibonacciService;
        }

        @Bean
        FibonacciUtil fibonacciUtil(){
            return new FibonacciUtil();
        }
    }
    // ...
}

这里的内嵌类Config,实际上和平时我们编写的 Spring 配置类的功能是相同的,所以只要在Config中提供测试所需的 bean 的工厂方法,就可以生成相应的上下文(ApplicationContext),并且在测试类中完成自动连接。

需要注意的是,这里的内嵌类Config不能是private的。

@SpringJUnitConfig

实际上@SpringJUnitConfig就是以下两个注解的组合注解:

  • @ExtendWith(SpringExtension.class)
  • @ContextConfiguration

因此之前的示例可以改写为:

@SpringJUnitConfig
public class TestFibonacciService7 {
	// ...
}

此外,@SpringJUnitConfig也包含locationsclasses属性,是@ContextConfiguration相应属性的别名。

@SpringBootTest

通常,使用我们前面介绍的上下文就可以测试 Spring 项目中绝大多数的功能,但有时候我们需要测试“更完整的功能”或是模拟“更真实”的运行情况。

此时就需要借助@SpringBootTest注解编写测试用例。

可以用@SpringBOotTest改写之前的示例:

@SpringBootTest(classes = {TestApplication.class})
public class TestFibonacciService2 {
    @Autowired
    private FibonacciService fibonacciService;
    // ...
}

@SpringBootTest同样需要指定测试相关的组件类型来生成上下文,但实际上更常见的是缺省classeslocations属性,让其自动检测以获取入口文件:

@SpringBootTest
public class TestFibonacciService2 {
	// ...
}

此时 Spring 会按照目录结构查找用@SpringBootApplication@SpringBootConfiguration标记的类(通常找到的是 Spring 的入口类)。

调用 main 方法

通常我们的入口类中的 main 方法是很简单的:

@SpringBootApplication
public class TestApplication {
    public static void main(String[] args) {
        SpringApplication.run(TestApplication.class, args);
    }
}

但有时候可能会添加一些额外代码,比如添加特殊的事件监听:

@SpringBootApplication
public class TestApplication {
    public static void main(String[] args) {
        SpringApplication application = new SpringApplication(TestApplication.class);
        application.addListeners((ApplicationListener<ApplicationStartedEvent>) event -> {
            System.out.println("ApplicationStartingEvent is called.");
        });
        application.run(args);
    }
}

默认情况下@SpringBootApplication编写的测试用例启动后并不会执行入口类的main方法,而是直接利用入口类创建一个新对象并运行。

因此在下面这个测试用例中不会看到任何事件监听器的输出:

@SpringBootTest
public class TestFibonacciService8 {
	// ...
}

可以通过修改useMainMethod属性改变这一行为:

@SpringBootTest(useMainMethod = SpringBootTest.UseMainMethod.ALWAYS)
public class TestFibonacciService9 {
	// ...
}

此时测试用例将通过main方法启动SpringApplication实例,所以可以看到相关监听器的输出。

useMainMethod属性有以下可选的值:

  • ALWAYS,总是通过main方法启动,如果缺少main方法,就报错。
  • NEVER,不使用main方法启动,改为使用一个专门用于测试的SpringApplication实例。
  • WHEN_AVAILABLE,如果入口类有main方法,就通过main方法启动,否则使用测试专用的SpringApplication实例启动。

@TestConfiguration

有时候,你可能需要在测试时加载一些“专门为测试添加的 bean”,此时可以使用@TestConfiguration

@SpringBootTest(useMainMethod = SpringBootTest.UseMainMethod.ALWAYS)
public class TestFibonacciService10 {
    @Autowired
    private String msg;	
    
    @TestConfiguration
    static class Config{
        @Bean String msg(){
            return "hello";
        }
    }
    
    @Test
    void testMsg(){
        Assertions.assertEquals("hello", msg);
    }
    // ...
}

此时,上下文中除了 SpringApplication 启动后利用自动扫描加载的 bean 以外,还将测试类中@TestConfiguration标记的内部类工厂方法返回的 bean 也加载到了上下文。

除了这种内部类以外,也可以单独定义类并使用@Import导入:

@TestConfiguration
public class MyTestConfig {
    @Bean
    String msg(){
        return "hello";
    }
}

@SpringBootTest(useMainMethod = SpringBootTest.UseMainMethod.ALWAYS)
@Import(MyTestConfig.class)
public class TestFibonacciService11 {
	// ...
}

此外,使用@TestConfiguration标记的类并不会被自动扫描识别和添加。

使用应用参数

如果应用启动时会添加某些参数,并且你希望针对这点进行测试,可以:

@SpringBootTest(args = "--app.test=one")
public class TestApplicationArgs {
    @Test
    void testArgs(@Autowired ApplicationArguments arguments){
        Assertions.assertTrue(arguments.getOptionNames().contains("app.test"));
        Assertions.assertTrue(arguments.getOptionValues("app.test").contains("one"));
    }
}

使用模拟环境测试

默认情况下,@SpringBootTest不会启动 Nginx 服务器,而是通过一个模拟环境进行测试。

在这种情况下,我们可以使用MockMVC测试我们的 Web 请求:

@SpringBootTest
@AutoConfigureMockMvc
public class TestFibonacciController {
    @Test
    void testFibonacci(@Autowired MockMvc mockMvc) throws Exception {
        ObjectMapper mapper = new ObjectMapper();
        int[] fibonacciArr = new int[]{1, 1, 2, 3, 5, 8};
        for (int i = 0; i < fibonacciArr.length; i++) {
            int n = i + 1;
            var targetResultStr = mapper.writeValueAsString(Result.success(fibonacciArr[i]));
            mockMvc.perform(MockMvcRequestBuilders.get("/fibonacci/" + n))
                    .andExpect(MockMvcResultMatchers.status().isOk())
                    .andExpect(MockMvcResultMatchers.content().json(targetResultStr, false));
        }
    }
}

需要注意的是,要使用 MockMVC,需要用@AutoConfigureMockMvc开启相关功能,否则无法注入MockMvc实例。

关于MockMVC 的更多介绍,见 Spring 官方文档。

运行服务器并测试

如果你需要真正地运行服务并测试网络请求,可以:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class TestFibonacciController2 {
    @Test
    void testFibonacci(@Autowired WebTestClient client) throws Exception {
        ObjectMapper mapper = new ObjectMapper();
        int[] fibonacciArr = new int[]{1, 1, 2, 3, 5, 8};
        for (int i = 0; i < fibonacciArr.length; i++) {
            int n = i + 1;
            var targetResultStr = mapper.writeValueAsString(Result.success(fibonacciArr[i]));
            client.get().uri("/fibonacci/" + n)
                    .exchange()
                    .expectStatus().isOk()
                    .expectBody().json(targetResultStr, false);
        }
    }
}

这里通过@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT),可以启动服务并随机监听一个端口用于测试网络请求和响应。

为了方便地调用HTTP客户端,这里注入了一个WebTestClient,这是一个用 Spring-webflux 实现的 Http 客户端。要使用这个客户端,需要添加依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

如果你因为某些原因不愿意为了测试加入这个依赖,可以使用TestRestTemplate,这是 Spring 提供的一个便利组件:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class TestFibonacciController3 {
    @Test
    void testFibonacci(@Autowired TestRestTemplate testRestTemplate) {
        int[] fibonacciArr = new int[]{1, 1, 2, 3, 5, 8};
        for (int i = 0; i < fibonacciArr.length; i++) {
            int n = i + 1;
            var body = testRestTemplate.getForObject("/fibonacci/" + n, Result.class);
            Assertions.assertEquals(body, Result.success(fibonacciArr[i]));
        }
    }
}

还可以使用固定端口进行测试:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
public class TestFibonacciController4 {
    // ...
}

此时会使用默认的8080端口或者application.properties中定义的server.port

webEnvironment属性有以下可选值:

  • MOCK,使用模拟环境。
  • RANDOM_PORT,使用随机端口运行服务。
  • DEFINED_PORT,使用固定端口运行服务。
  • NONE,使用 SpringApplication 加载一个 ApplicationContext,但不提供任何Web环境。

自动配置的测试

有时候,并不需要对全部的功能进行测试,只希望对其中的一部分测试(比如 redis、数据库、MVC等),对此 Spring 提供了一个spring-boot-test-autoconfigure模块,该模块包含一系列的@xxxTest注解,以及@AutoConfigureXXX注解,利用这些注解可以仅加载相关的组件进行测试。

具体见官方文档。

MockBean

有时候,在测试中你需要“模拟”某些组件,比如说某个功能依赖于远程调用,但是该功能尚未完成开发:

@Service
public class RemoteServer {
    public enum Weather{
        RAIN,
        SUNNY,
        CLOUDY
    }

    /**
     * 通过远程服务查询当前天气
     * 该接口还未完成
     * @return
     */
    public Weather getWeather(){
        return null;
    }
}

@Service
public class UserService {
    @Autowired
    private RemoteServer remoteServer;

    /**
     * 生成一段用户欢迎信息
     *
     * @return
     */
    public String getHelloMsg() {
        var weather = remoteServer.getWeather();
        var msg = new StringBuilder();
        switch (weather) {
            case RAIN -> msg.append("今天有雨,记得带伞。");
            case CLOUDY -> msg.append("今天多云,可以出去浪。");
            case SUNNY -> msg.append("阳关有点强烈,记得防晒喔。");
            default -> msg.append("我也不清楚天气状态,自己看天气预报吧。");
        }
        return msg.toString();
    }
}

这样的情形下我们可以“假设”未完成的方法调用会返回某个值来进行测试:

@SpringBootTest
public class TestUserService {
    @Autowired
    private UserService userService;
    @MockBean
    private RemoteServer remoteServer;

    @Test
    void testGetHelloMsg() {
        BDDMockito.given(this.remoteServer.getWeather()).willReturn(RemoteServer.Weather.RAIN);
        var msg = userService.getHelloMsg();
        Assertions.assertEquals("今天有雨,记得带伞。", msg);
    }
}

在上面这个示例中,用@MockBean标记充当“测试桩”的 bean,且用BDDMockito.given(...).willReturen(...)的方式为其getWeather方法指定了一个固定返回值。上下文中的RemoteServer bean 将被我们这里设置的这个测试桩 bean 取代,因此注入UserService并调用userService.getHelloMsg()时,会得到我们想要的结果。

The End,谢谢阅读。

本文的完整示例代码可以从这里获取。

参考资料

  • JUnit 5 User Guide
  • JUnit 5自定义扩展 - 知乎 (zhihu.com)
  • Testing in Spring Boot | Baeldung
  • MockMVC
  • WebTestClient
  • Spring TestContext 框架
  • Spring WebFlux :: Spring Framework
  • Spring WebFlux 教程 - 知乎 (zhihu.com)
  • SpringRunner vs MockitoJUnitRunner | Baeldung

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

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

相关文章

数据中心机柜PDU应该怎么选?

数据中心是国家确定的“新基建”七大领域之一。数据中心在国民经济和社会发展中所起的作用越来越重要&#xff0c;数据中心已经成为了各行各业的关键基础设施&#xff0c;为经济转型升级提供了重要支撑。在庞杂的数据中心&#xff0c;服务器和交换器担负着传输数据的重要责任&a…

【论文阅读-人机交互】通过用户参与来缓解人工智能决策中的知识失衡问题

Title: Mitigating knowledge imbalance in AI-advised decision-making through collaborative user involvement From: International Journal of Human - Computer Studies Link: https://doi.org/10.1016/j.ijhcs.2022.102977 目录 1 绪论2 方法2.1 假设2.2. 实验任务及研究…

【单片机】STM32F103C8T6 最小系统板原理图

STM32F103C8T6是一款基于ARM Cortex-M3内核的32位微控制器&#xff0c;由STMicroelectronics&#xff08;ST&#xff09;公司生产。它是STMicroelectronics的STM32系列微控制器中的一员&#xff0c;被广泛应用于嵌入式系统和电子设备中。 STM32F103C8T6单片机的主要特点和资源…

点云深度学习系列博客(七): 针对点云的数据增强技术

好长时间不更新博客了&#xff0c;入职以后突然就变忙了&#xff0c;确实有心无力。最近做一个点云数据增强的项目&#xff0c;搞了一个简单的前期调研&#xff0c;趁着最近几天不太忙&#xff0c;凑一篇博客出来&#xff0c;保一下博客专家资格... 一. 简介 我们在利用深度学…

TDesign电商小程序模板解析02-首页功能

目录 1 home.json2 goods-list组件3 goods-card组件总结 上一篇我们搭建了底部的导航条&#xff0c;这一篇来拆解一下首页的功能。首页有如下功能 可以进行搜索显示轮播图横向可拖动的页签图文卡片列表 1 home.json 因为是要使用组件库的组件搭建页面&#xff0c;自然是先需要…

Git第二章、多人协作

一、多人协作一 目前&#xff0c;我们所完成的工作如下&#xff1a; • 基本完成 Git 的所有本地库的相关操作&#xff0c;git基本操作&#xff0c;分支理解&#xff0c;版本回退&#xff0c;冲突解决等等 • 申请码云账号&#xff0c;将远端信息clone到本地&#xff0c;以及推…

简单认识Nginx网络服务

文章目录 一、简介1、概括2、Nginx和Apache的差异3、Nginx优于Apache的优点 二、编译安装nginx 服务1、在线安装nginx2、 nginx编译安装&#xff08;1&#xff09;、关闭防火墙&#xff0c;将安装nginx所需软件包传到/opt目录下&#xff08;2&#xff09;、#nginx的配置及运行需…

基于分时电价和蓄电池控制策略用电优化研究(matlab代码)

目录 1 主要内容 温控负荷模型 蓄电池模型 2 部分代码 3 程序结果 4 下载链接 点击直达&#xff01; 1 主要内容 该程序复现《基于需求侧家庭能量管理系统用电优化研究》中第三章模型&#xff0c;题目是《基于分时电价和蓄电池控制策略用电优化研究》&#xff0c;该部…

Spring Boot初阶篇笔记

SpringBoot笔记 SpringBoot官方文档 一、SpringBoot的常用注解 ConfigurationProperties、PropertySource、ImportResource的区别 1.ConfigurationProperties: ConfigurationProperties:告诉SpringBoot将本类中的所有属性与配置文件中的相关属性进行绑定; 如&#xff1a;C…

Linux权限维持方法论

Linux权限维持方法论 1.创建超级用户2.SUID后门权限维持3.Strace监听后门4.rookit后门 1.创建超级用户 例如&#xff1a; 创建一个用户名guest&#xff0c;密码123456的root用户 useradd -p openssl passwd -1 -salt salt 123456 guest -o -u 0 -g root -G root -s /bin/bas…

【C语言初阶】带你玩转C语言中的数组,并逐步实现冒泡排序,三子棋,扫雷

君兮_的个人主页 勤时当勉励 岁月不待人 C/C 游戏开发 数组的使用 前言一维数组1.一维数组的定义数组的分类 2.数组的初始化第一种越界情况 3.数组的使用数组的下标&#xff1a;第二种越界情况 4.数组在内存中的存储 二维数组1.二维数组的创建2.二维数组的初始化3.二维数组的…

vue3新特性与vue2的不同点对比

前端必备工具推荐网站(免费图床、API和ChatAI等实用工具): http://luckycola.com.cn/ 一、vue3是什么? Vue 是一款用于构建用户界面的 JavaScript 框架。它基于标准 HTML、CSS 和 JavaScript 构建&#xff0c; 并提供了一套声明式的、组件化的编程模型&#xff0c;帮助你高效…

计算机网络管理-实验4(三) 使用Wireshark 抓取MIB-2中sysUpTime对象的SNMP报文管理信息结构

⬜⬜⬜ &#x1f430;&#x1f7e7;&#x1f7e8;&#x1f7e9;&#x1f7e6;&#x1f7ea;(*^▽^*)欢迎光临 &#x1f7e7;&#x1f7e8;&#x1f7e9;&#x1f7e6;&#x1f7ea;&#x1f430;⬜⬜⬜ ✏️write in front✏️ &#x1f4dd;个人主页&#xff1a;陈丹宇jmu &am…

【win11+Visual Studio 2019 配置 PCL 1.12.1 的经验总结分享】

点云pc库的下载与安装参考另外一篇文章&#xff0c;链接&#xff1a; https://blog.csdn.net/weixin_47869094/article/details/131270772?spm1001.2014.3001.5501 各种教程里面这都很好&#xff0c;就不赘述了&#xff0c;当然&#xff0c;这里也给出一个个人认为不错的安装…

移动互联网行业就业率除车载外,还有“该”的岗位在暴涨!

从2013年进入到移动互联网时代&#xff0c;在Android系统广泛应用在手机厂商中&#xff0c;App承载了我们生活工作的方方面面&#xff0c;原来需要在PC或者线下才能做的事情&#xff0c;现在点一点手指就可以做到了。这类方便也带来更多的系统安全危害。正所谓魔高一尺道高一丈…

一.《UE4奥丁》人物最大属性

​寻找突破口 1.续上节课,此时看到标题,有基础的同学第一反应就是,老师用CE搜索血量,通过改变就能找到&#xff01; 2.额,有这么简单吗&#xff01; 3.既然写了这个帖子,肯定是有原因的 4.为了方便学习,我们就先按照同学们的思路来试一试,能不能找到最大属性,比如最大血&am…

mysql索引方面的知识

1. 查看表的索引 show index from 表名 ex: 重点看Key_name这一列的值, 可以看到只有一个主键, 目前该表还没有创建索引, 接下来进行索引的创建 2.给表添加索引 create index idx_time_classtype_sip_sport on event_20230508(time,classtype,sip,sport) 说明: 上面index后…

【论文精读】DELS-MVS

今天读的是发表在WACV2023上的文章&#xff0c;第一作者来自于格拉茨技术大学。 文章链接&#xff1a;DELS-MVS: Deep Epipolar Line Search for Multi-View Stereo 文章目录 Abstract1. Introduction2. Related Work3. Algorithm3.1 Depth estimation via epipolar residual3.…

【换脸详细教程】手把手教你进行AI换脸:换脸流程及源码详解

目录 1. 换脸基本原理2 人脸检测及可视化3. 人脸轮廓点检测及可视化4. 人脸图像变换--仿射变换5. 生成遮罩并直接替换人脸6. 人脸颜色校正 最近AI换脸貌似比较火爆&#xff0c;就稍微研究了一下相关了内容。AI换脸是一个娱乐性比较强的应用&#xff0c;这种错位感让人觉得非常有…

搭建高性能数据库集群之一:主从复制

一、概述 1. 数据库主从概念、优点、用途   主从数据库是什么意思呢&#xff0c;主是主库的意思&#xff0c;从是从库的意思。数据库主库对外提供读写的操作&#xff0c;从库对外提供读的操作。 数据库为什么需要主从架构呢&#xff1f;   高可用&#xff0c;实时灾备&am…