SpringBoot——单元测试实践总结

news2024/11/19 20:23:31

文章目录

  • 单元测试
    • 概念
    • 作用
    • 黑白盒
      • 黑盒测试
      • 白盒测试
      • 逻辑覆盖
      • 1、语句覆盖
      • 2、判定覆盖
      • 3、条件覆盖
      • 4、条件/判定覆盖
      • 5、条件组合覆盖
      • 6、路径覆盖
  • SpringBoot工程单测介绍
    • pom依赖
      • 注意:
    • Idea结构
      • 创建路径
      • 创建类和方法
    • Controller层单测
      • 被测代码
      • 测试代码
    • Service层单测
      • 被测代码
      • 测试代码
  • 单测规约
  • PowerMock框架
    • mock介绍
      • mock概念
      • mock作用
    • PowerMock介绍
      • PowerMock pom依赖
        • 注意:
      • PowerMock 功能
      • PowerMock 常用mock示例
        • mock模板
        • mock私有构造方法
        • mock私有方法和私有属性
        • mock静态方法
        • mock配置
        • mock对象类的公有方法值返回
  • 排雷
    • @SpringBootTest
      • 问题:
      • 解决方案:
    • SpringBoot+PowerMock+Mockito结合使用
      • 问题:
      • 解决方案:
    • 构造函数注入类
      • 问题:
      • 解决方案:
    • 静态方法mock时报错org.powermock.api.mockito.ClassNotPreparedExceptionnot
      • 问题:
      • 解决方案:
    • mybatis报错MybatisPlusException
      • 问题:
      • 解决方案:
  • 参考

单元测试

概念

在计算机编程中,单元测试(英语:Unit Testing)又称为模块测试,是针对程序模块(软件设计的最小单位)来进行正确性检验的测试工作。 程序单元是应用的最小可测试部件。在过程化编程中,一个单元就是单个程序、函数、过程等;对于面向对象编程,最小单元就是方法,包括基类(超类)、抽象类、或者派生类(子类)中的方法。——维基百科

作用

  1. 提质
    单测可以一定程度上减少潜在bug,提高代码质量。单测不仅解决覆盖率问题,也可以覆盖代码块中的一些边界和异常处理问题。
  2. 重构
    单测可以为后续其他小伙伴修改、重构代码保驾护航,因为你只要敢乱改代码,单测就敢给你报错。
  3. 调试
    单测有助于代码调试,我们可以按照需求进行依赖类、方法和参数的mock,无需下游类、方法的真实调用。
  4. CodeReview
    单测也是一种自我CodeReview的过程。对功能单元的主流程、分支、边界以及异常情况进行分别测试,有助于复查代码的逻辑设计是否合理。反向督促自己提高编码质量意识。特别是,一个方法几百行,写单测时,你会发现这一坨,为何不拆一拆?

黑白盒

黑盒测试

  黑盒测试又称为功能测试数据驱动测试,测试过程中,程序看作成一个黑色盒子,看不到盒子内部代码结构。
  黑盒测试主要根据功能需求设计测试用例进行测试,是一种从软件外部实施的测试方式。多次输入参数,测试查看程序是否正常或达到预期。
  黑盒只知道软件的功能(能干什么),但是不知道软件的实现(怎么干的)。

白盒测试

  白盒测试又称为结构测试逻辑驱动测试,测试过程中,程序看作一个透明盒子,能够看清盒子内部的代码和结构,这样测试人员对程序代码的逻辑有所知晓。
  穷举路径的方式传参,检查代码所有结构是否正常或符合预期。单元测试属于白盒测试。
  白盒知道软件的实现(怎么干的),不需要管软件的功能(能干什么)。

逻辑覆盖

1、语句覆盖

  程序每条可执行语句至少执行一次,即测试用例覆盖所有语句。

2、判定覆盖

  也称为分支覆盖,针对判定表达式,true或false两种真假判定,程序中每一个判断的分支至少经历一次。
  比如,判定表达式:a > 0 && b > 0
  设计测试数据

a > 0 && b > 0(判定表达式的值为“真”)
a <= 0 && b <= 0(判定表达式的值为“假”)

  满足判定的所有分支覆盖(此时真、假分支都覆盖)。

3、条件覆盖

  针对判断语句中的条件,程序中每个判断中的每个条件取值至少满足一次,针对条件语句。
  比如,判定表达式:a > 0 && b > 0
  设计测试数据

a <= 0 && b <= 0(判定表达式的值为“假”)
a > 0 && b <= 0(判定表达式的值为“假”)

  保证每个条件取值一次,而不管分支是否覆盖全面(此时只覆盖假分支)。

4、条件/判定覆盖

  判定条件中所有可能条件成立与否至少执行一次取值、所有真假判断的可能结果至少执行一次。
  比如,判定表达式:a > 0 && b > 0
  设计测试数据

a > 0 && b > 0(判定表达式的值为“真”)
a <= 0 && b <= 0(判定表达式的值为“假”)

5、条件组合覆盖

  所有可能的条件取值组合至少执行一次。
  比如,判定表达式:a > 0 && b > 0
  设计测试数据

a > 0 && b > 0(判定表达式的值为“真”)
a <= 0 && b <= 0(判定表达式的值为“假”)
a > 0 && b <= 0(判定表达式的值为“假”)
a <= 0 && b > 0(判定表达式的值为“假”)

  判定中所有可能的条件组合。

6、路径覆盖


  所有可能执行的路径。

SpringBoot工程单测介绍

pom依赖

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

注意:

  1. 该依赖版本一般是跟着对应的SpringBoot版本走,不需要手动指定。
  2. 该依赖会自动引入相关依赖:
    JUnit
    Mockito
    Spring Test
    … …

Idea结构

创建路径

  与src/main同级目录,创建src/test,其他类路径与main包中保持一致。(可通过创建类的快捷方法自动创建路径)

创建类和方法

创建类对应单测类的快捷方法:只需将双击这个类,鼠标右键,然后选择go to到Test。

选择要测试的方法,同时,可以选择JUnit版本等,点击OK

Controller层单测

被测代码

@Slf4j
@RestController
@RequestMapping("/demo")
@Api(value = "DemoController", tags = "Demo管理模块")
public class DemoController implements DemoApi {
    /**
     * service
     */
    @Autowired
    private DemoService demoService;


    @Override
    @ApiOperation(value = "新增", notes = "新增")
    @PostMapping("/create")
    public Boolean add(@RequestHeader("appCode") String appCode,
                                     @RequestBody DemoDTO demoDTO) {
        boolean addFlag = demoService.add(demoDTO);
        if (addFlag) {
            // 刷新资源
          demoService.refreshMap(appCode);
        }
        return addFlag;
    }


    @Override
    @ApiOperation(value = "修改", notes = "修改")
    @PostMapping("/update")
    public Boolean update(@RequestHeader("appCode") String appCode,
                                        @RequestBody DemoDTO demoDTO) {
      
        boolean addFlag = demoService.update(demoDTO);
        if (addFlag) {
            // 刷新资源
          demoService.refreshMap(appCode);
        }
        return addFlag;
    }


    @Override
    @ApiOperation(value = "删除", notes = "删除")
    @DeleteMapping("/delById")
    public Boolean deleteById(@RequestHeader("appCode") String appCode, @RequestParam Long id) {
        boolean deleteFlag = demoService.deleteById(id);
        if (deleteFlag) {
            // 刷新资源
          demoService.refreshMap(appCode);
        }
        return addFlag;
    }


    @Override
    @ApiOperation(value = "列表", notes = "列表")
    @GetMapping("/list")
    public List<DemoVO> list() {
        return demoService.list();
    }
 }

测试代码

  1. 单独组件测试,无需使用@SpringBootTest进行整体上下文启动,使用@RunWith(SpringRunner.class)运行测试用例。
  2. 使用@InjectMock注入controller类。
  3. 使用@Mock注解mock service类。
@RunWith(SpringRunner.class)
public class DemoControllerTest {
    /**
     * mock mvc
     */
    private MockMvc mockMvc;


    /**
     * 注入实例
     */
    @InjectMocks
    private DemoController demoController;


    /**
     * service mock
     */
    @Mock
    private DemoService demoService;
    
    /**
     * appCode
     */
    private String appCode;


    /**
     * before设置
     */
    @Before
    public void setUp() {
        //初始化带注解的对象
        MockitoAnnotations.openMocks(this);
        //构造mockmvc
        mockMvc = MockMvcBuilders.standaloneSetup(demoController).build();
        //appCode
        appCode = "AppCode_test";
    }


    /**
     * 测试testAdd
     */
    @Test
    public void testAdd() throws Exception {
        //构建dto
        DemoDTO demoDTO = new DemoDTO();
        //setId
        demoDTO.setId(-1L);
        //setName
        demoDTO.setName("test");
        //mock service方法
   		PowerMockito.when(demoService.add(demoDTO)).thenReturn(true);
        //构造body
        String body = JSONObject.toJSONString(demoDTO);
        //执行mockmvc
        this.mockMvc.perform(MockMvcRequestBuilders.post("/demo/create")
                //传参
                .header("appCode", appCode).content(body).contentType(MediaType.APPLICATION_JSON_VALUE))
                //mock返回
                .andExpect(status().isOk()).andDo(MockMvcResultHandlers.print()).andReturn();
    }


    /**
     * 测试testUpdate
     */
    @Test
    public void testUpdate() throws Exception {
          //构建dto
        DemoDTO demoDTO = new DemoDTO();
        //setId
        demoDTO.setId(-1L);
        //setName
        demoDTO.setName("test");
        //mock service方法
        PowerMockito.when(demoService.update(demoDTO)).thenReturn(true);
        //构造body
        String body = JSONObject.toJSONString(demoDTO);
        //执行mockmvc
        this.mockMvc.perform(MockMvcRequestBuilders.post("/demo/update")
                //传参
                .header("appCode", appCode).content(body).contentType(MediaType.APPLICATION_JSON_VALUE))
                //mock返回
                .andExpect(status().isOk()).andDo(MockMvcResultHandlers.print()).andReturn();
    }


    /**
     * 测试testDelete
     */
    @Test
    public void testDelete() throws Exception {
        //Id
        Long id = 1000L;
        //mock service方法
        PowerMockito.when(demoService.deleteById(id)).thenReturn(true);
        //执行mockmvc 方法一
//        this.mockMvc.perform(MockMvcRequestBuilders.delete("/demo/delById?id={id}",id)
//                //传参
//                .header("appCode", appCode).contentType(MediaType.APPLICATION_JSON_VALUE))
//                //mock返回
//.andExpect(status().isOk()).andDo(MockMvcResultHandlers.print()).andReturn();
//    }
		//方法二
        this.mockMvc.perform(MockMvcRequestBuilders.delete("/demo/delById")
                //传参
                .header("appCode", appCode).param("id", "1000").contentType(MediaType.APPLICATION_JSON_VALUE))
                //mock返回
                .andExpect(status().isOk()).andDo(MockMvcResultHandlers.print()).andReturn();
    }


    /**
     * 测试testList
     */
    @Test
    public void testList() throws Exception {
        this.mockMvc.perform(MockMvcRequestBuilders.get("/demo/list")
                //传参
                .contentType(MediaType.APPLICATION_JSON_VALUE))
                //mock返回
                .andExpect(status().isOk()).andDo(MockMvcResultHandlers.print()).andReturn();
    }
}

Service层单测

被测代码

@Service 
public class MenuService { 
    @Autowired 
    private MenuMapper menuMapper; 
    
    @Transactional(readOnly = true) 
    public List<Menu> listMenus() { 
        final List<Menu> result = menuMapper.list(); 
        return result; 
    }
} 

测试代码

  1. 通过 @TestConfiguration 创建一个测试用配置,该配置中提供了一个 MenuService Bean的声明。
  2. 该注解的使用有以下几个注意点:被注解的类须是 static 的,且不能是 private 的。建议用在使用在内部类上,否则所定义的 Bean 将不会被自动加载。须通过以下方式之一进行加载
@Import(MenuServiceTestConfig.class)
@ContextConfiguration(classes =MenuServiceTestConfig.class)
@Autowired
//此处通过 @Autowired 自动注入的是上面通过 @TestConfiguration 声明的 Bean。
@RunWith(SpringRunner.class) 
public class MenuServiceTest { 
    @TestConfiguration 
    static class MenuServiceTestConfig { 
        @Bean 
        public MenuService mockMenuService() { 
            return new MenuService(); 
        } 
    }
    @Autowired 
    private MenuService MenuService; 
    @MockBean 
    private MenuMapper MenuMapper; 
    @Test 
    public void listMenus() { 
        List<Menu> menus = new ArrayList<Menu>() {{ 
            this.add(new Menu()); 
        }}; 
        Mockito.when(menuMapper.list()).thenReturn(menus); 
        List<Menu> result = menuService.listMenus(); 
        Assertions.assertThat(result.size()).isEqualTo(menus.size()); 
    } 
}

单测规约

  1. 【强制】好的单元测试必须遵守AIR原则。
    说明:单元测试在线上运行时,感觉像空气(AIR)一样并不存在,但在测试质量的保障上,却是非常关键的。好的单元测试宏观上来说,具有自动化、独立性、可重复执行的特点。
AAutomatic(自动化)
IIndependent(独立性)
RRepeatable(可重复)
  1. 【强制】单元测试应该是全自动执行的,并且非交互式的。
    说明:测试用例通常是被定期执行的,执行过程必须完全自动化才有意义。输出结果需要人工检查的测试不是一个好的单元测试。单元测试中不准使用System.out来进行人肉验证,必须使用assert来验证。
  2. 【强制】保持单元测试的独立性。
    说明:为了保证单元测试稳定可靠且便于维护,单元测试用例之间决不能互相调用,也不能依赖执行的先后次序。
    反例:method2需要依赖method1的执行,将执行结果作为method2的输入。
  3. 【强制】单元测试是可以重复执行的,不能受到外界环境的影响。
    说明:单元测试通常会被放到持续集成中,每次有代码check in时单元测试都会被执行。如果单测对外部环境(网络、服务、中间件等)有依赖,容易导致持续集成机制的不可用。
    正例:为了不受外界环境影响,要求设计代码时就把SUT的依赖改成注入,在测试时用spring 这样的DI框架注入一个本地(内存)实现或者Mock实现。
  4. 【强制】对于单元测试,要保证测试粒度足够小,有助于精确定位问题。单测粒度至多是类级别,一般是方法级别。
    说明:只有测试粒度小才能在出错时尽快定位到出错位置。单测不负责检查跨类或者跨系统的交互逻辑,那是集成测试的领域。
  5. 【强制】核心业务、核心应用、核心模块的增量代码确保单元测试通过。
    说明:新增代码及时补充单元测试,如果新增代码影响了原有单元测试,请及时修正。
  6. 【强制】单元测试代码必须写在如下工程目录:src/test/java,不允许写在业务代码目录下。
    说明:源码构建时会跳过此目录,而单元测试框架默认是扫描此目录。

PowerMock框架

mock介绍

mock概念

mock是指在测试过程中,创建一个虚拟的对象来模拟指定对象的行为。

mock作用

  1. service-A依赖于service-B,测试service-A时,service-B还未开发完,通过mock模拟service-B的一些行为达到测试效果。
  2. 创建私有构造方法的对象,没法直接构建,可以通过mock构建。
  3. 被测试的模块需连接数据库等操作,测试时无法保证一定能连接数据库,可通过mock实现。
  4. … …

PowerMock介绍

  1. PowerMock时一个Java单测模拟的框架,扩展了EasyMock和Mockito框架。
  2. PowerMock通过提供定制的类以及一些字节码篡改技巧进行模拟。
  3. PowerMock可模拟静态方法、私有方法、构造方法、final方法等。
  4. PowerMock支持JUnit和TestNG。

PowerMock pom依赖

<properties>
    <powermock.version>2.0.9</powermock.version>
</properties>
<dependencies>
   <dependency>
      <groupId>org.powermock</groupId>
      <artifactId>powermock-module-junit4</artifactId>
      <version>${powermock.version}</version>
      <scope>test</scope>
   </dependency>
   <dependency>
      <groupId>org.powermock</groupId>
      <artifactId>powermock-api-mockito2</artifactId>
      <version>${powermock.version}</version>
      <scope>test</scope>
   </dependency>
</dependencies>

注意:

此依赖适合JUnit4.4及以上。

PowerMock 功能

  • mock constructors
  • mock final method or classes
  • mock private methods
  • mock static methods
  • mock java system classes
    各位小伙伴有兴趣的自己对着官网研究,版本重点介绍一些常用的mock功能。

PowerMock 常用mock示例

mock模板

@RunWith(PowerMockRunner.class)
@PowerMockRunnerDelegate(SpringRunner.class)
@PrepareForTest({XxxUtils.class})
public class DemoServiceImplTest {
    /**
     * 注入service
     */
    @InjectMocks
    private DemoServiceImpl demoService;

    /**
     * mapper
     */
    @MockBean
private DemoMapper demoMapper;

@Before
public void setUp() {
      //构建mybatis缓存
      TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new MybatisConfiguration(), ""), DemoEntity.class);
}

@Test
public void testCreateDemo() {
   //todo
}
}

mock私有构造方法

以部门封装的Result类为例,其中一些private构造方法无法构造参数,需mock构造。

Result<List<UserInfoDTO>> result = Whitebox.invokeConstructor(Result.class, "200", "", true, userInfoDTOList);

mock私有方法和私有属性

//mock 当前service类
ServiceA serviceMock= PowerMockito.mock(ServiceA.class);
//内部调用其他service需设置非公共成员
Whitebox.setInternalState(serviceMock, "serviceB", serviceB);
//mock调用其他service方法
PowerMockito.when(serviceB.getStr("xxx")).thenReturn("xxxxx");
//调用内部私有方法
Whitebox.invokeMethod(serviceMock, "privateMethod1", demoDto);

说明:

  1. ServiceA类中注入了ServiceB类,并调用了getStr(String str)方法。
  2. ServiceA类中私有方法privateMethod1,传参DemoDto。

mock静态方法

  1. 添加需要被测试的静态方法注解
@PrepareForTest({StaticXXX.class})
  1. 测试mock调用
//mock静态类
PowerMockito.mockStatic(StaticXXX.class);
//mock调用静态方法
PowerMockito.when(StaticXXX.method01("param01", "param02")).thenReturn(xxx);

mock配置

@MockBean
private DemoProperties demoProperties ;
  1. 使用@MockBean引入配置类
  2. 调用塞值
//构造demoProperties 
DemoProperties .DemoFirstProperties demoFirstProperties = new DemoProperties .DemoFirstProperties ();
//塞值
demoFirstProperties .setFirstParams("xxxx");
//mock properties
PowerMockito.when(demoProperties .getDemoFirstProperties ()).thenReturn(demoFirstProperties );

mock对象类的公有方法值返回

  1. 对象类
public class SmsResponse implements Serializable {
    /**
     * 编码 1-成功
     */
    private Integer code;
    /**
     * 返回内容
     */
    private String msg;
    public boolean isSuccess(){
        return null != code && 200 == code;
    }
    public Integer getCode() {
        return code;
    }
    public void setCode(Integer code) {
        this.code = code;
    }
    public String getMsg() {
        return msg;
    }
    public void setMsg(String msg) {
        this.msg = msg;
    }
}
  1. 此类中的isSuccess()方法调用返回值在代码中使用
// 发送短信
SmsResponse smsResponse = this.smsNoticeService.sendSms(sms);
if (smsResponse.isSuccess()) {
    if (Objects.nonNull(noticeTimes) && Objects.nonNull(smsNotice)) {
        // 短信发送成功,更新通知标识
        noticeTimes.put(NoticeChannelEnum.SMS.getDesc(),smsNotice);
    }
    result.put(MSG,"");
    return result;
}

此时若想进入到if分支内,就需要mock操作

SmsResponse smsResponse = PowerMockito.mock(SmsResponse.class);
PowerMockito.when(smsResponse.isSuccess()).thenReturn(true);
when(this.smsNoticeService.sendSms(any(Sms.class))).thenReturn(smsResponse);

排雷

@SpringBootTest

问题:

  1. @SpringBootTest会扫描应用程序的Spring配置,并构建完整的Spring Context。每次执行一个类的单元测试,都需要启动整个上下文,单测速度慢!
  2. @SpringBootTest加载Spring上下文时,可能会因为服务中使用的数据库、MQ、缓存等配置加载失败而导致测试类启动。
  3. @SpringBootTest更适合功能集成测试。

解决方案:

使用@RunWith(SpringRunner.class)声明该测试是在Spring环境中进行,这样Spring相关注解就会被识别生效。

SpringBoot+PowerMock+Mockito结合使用

问题:

结合powermock使用时,容易造成一些类和方法的冲突,导致方法找不到。

解决方案:

依赖版本对应:

MockitoPowerMock
2.8.9+2.x
2.8.0-2.8.91.7.x
2.7.51.7.0RC4
2.4.01.7.0RC2
2.0.0-beta - 2.0.42-beta1.6.5-1.7.0RC
1.10.8 - 1.10.x1.6.2 - 2.0
1.9.5-rc1 - 1.9.51.5.0 - 1.5.6
1.9.0-rc1 & 1.9.01.4.10 - 1.4.12
1.8.51.3.9 - 1.4.9
1.8.41.3.7 & 1.3.8
1.8.31.3.6
1.8.1 & 1.8.21.3.5
1.81.3
1.71.2.5

构造函数注入类

问题:

有一些service类中注入的其他类,是通过构造函数注入,而非@Autowired或者@Resource注解。如

@Service 
public class MenuService { 
   
    private MenuMapper menuMapper; 
@Autowired 
public MenuService(final MenuMapper menuMapper) {
    this.menuMapper = menuMapper;
}

    @Transactional(readOnly = true) 
    public List<Menu> listMenus() { 
        final List<Menu> result = menuMapper.list(); 
        return result; 
    }
} 

解决方案:

构造service

@RunWith(SpringRunner.class) 
public class MenuServiceTest { 

private static final MenuMapper menuMapper = Mockito.mock(MenuMapper.class);

    @TestConfiguration 
    static class MenuServiceTestConfig { 
        @Bean 
        public MenuService mockMenuService() { 
            return new MenuService(menuMapper); 
        } 
    }
    @Autowired 
    private MenuService MenuService; 
    @MockBean 
    private MenuMapper MenuMapper; 
    @Test 
    public void listMenus() { 
        List<Menu> menus = new ArrayList<Menu>() {{ 
            this.add(new Menu()); 
        }}; 
        Mockito.when(menuMapper.list()).thenReturn(menus); 
        List<Menu> result = menuService.listMenus(); 
        Assertions.assertThat(result.size()).isEqualTo(menus.size()); 
    } 
}

静态方法mock时报错org.powermock.api.mockito.ClassNotPreparedExceptionnot

问题:

mock静态方法时,报错该静态类prepared for test

解决方案:

  1. 确认类上是否添加@PrepareForTest({Xxx.class})注解进行静态类的准备。
  2. 确认使用PowerMock时,是否使用了@RunWith(PowerMockRunner.class)运行环境。

mybatis报错MybatisPlusException

问题:

使用到lambda表达式有set方法的地方,单测报错com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: can not find lambda cache for this entity

解决方案:

在单测类中@Before方法中加入代码手动触发缓存信息收集。

@Before
public void setUp() {
    //构建mybatis缓存
    TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new MybatisConfiguration(), ""), XxxEntity.class);
}

其中,XxxEntity.class为表实体类。

参考

  1. 单测规约
  2. junit官网
  3. spring-boot-testing
  4. mybatis-plus代码库

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

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

相关文章

springboot集成hadoop3.2.4HDFS

前言 记录springboot集成hadoop3.2.4版本&#xff0c;并且调用HDFS的相关接口&#xff0c;这里就不展示springboot工程的建立了&#xff0c;这个你们自己去建工程很多教程。 一、springboot配置文件修改 1.1 pom文件修改 <!-- hadoop依赖 --><dependency><gro…

【从零开始学Skynet】基础篇(八):简易留言板

这一篇我们要把网络编程和数据库操作结合起来&#xff0c;实现一个简单的留言板功能。 1、功能需求 如下图所示&#xff0c;客户端发送“set XXX”命令时&#xff0c;程序会把留 言“XXX”存入数据库&#xff0c;发送“get”命令时&#xff0c;程序会把整个留言板返回给客户端。…

怎么把视频转成mp3音频,下面有四个方法

你有没有遇到过这种情况&#xff0c;看了一部电影或者纪录片&#xff0c;里面的背景音乐或者对白让你很感动&#xff0c;但是我们只需要其中的音频部分&#xff0c;比如在手机上收听音乐或者创作自己的音频内容。这时候我们可以先把视频保存下来&#xff0c;然后通过视频转音频…

光耦继电器工作原理及优点概述

光耦继电器是一种电子元器件&#xff0c;也是固态继电器的一种&#xff0c;其主要作用是隔离输入与输出电路&#xff0c;用于保护或者控制电路的正常工作。 光耦继电器工作原理是利用光电转换器将外界信号转化为光信号&#xff0c;通过光纤传输到另一端&#xff0c;再由另一端的…

【C生万物】 数组篇

欢迎来到 Claffic 的博客 &#x1f49e;&#x1f49e;&#x1f49e; 前言&#xff1a; 这个专栏好久没更新了&#xff0c;今日诗兴大发&#xff0c;打算尽快完成这个专栏&#xff0c;这期讲数组。 目录 Part1:一维数组 1.创建 2.初始化 3.使用 4.在内存中的存储 Part2:二…

安卓开发学习记录(续)

文章目录十一、综合训练&#xff08;购物车功能&#xff09;十二、内容提供者Provider十一、综合训练&#xff08;购物车功能&#xff09; 实现功能&#xff1a; 手机商品页面展示&#xff0c;加入购物车功能&#xff0c;商品详情页面&#xff0c;清空购物车&#xff0c;删除购…

C++算法初级9——递归

C算法初级9——递归 文章目录C算法初级9——递归递归求阶乘递归求斐波那契数列递归&#xff0c;简单地来说&#xff0c;就是一个函数自己调用自己。函数f()就好像是工厂中生产零件的模板&#xff0c;每次我们调用函数f()的时候&#xff0c;都会依照模板生产一个新的零件&#x…

项目4:后台管理的开发和使用(前端)

项目4&#xff1a;后台管理的开发和使用&#xff08;前端&#xff09; 1.npm包管理器的基本学习 2.利用现成后台管理系统开发 3.后台管理系统的路由配置 4.后台管理系统的地址访问配置 5.前后端联调 6.完善积分等级的前端系统 7.对前端系统的全面分析&#xff08;Vue组件…

跳槽进阿里了,其实也没那么难...

对于很多没有学历优势的人来说&#xff0c;面试大厂是非常困难的&#xff0c;这对我而言&#xff0c;也是一样&#xff0c;出身于二本&#xff0c;原本以为就三点一线的生活度过一生&#xff0c;直到生活上的变故&#xff0c;才让我有了新的想法和目标&#xff0c;因此我这个二…

【C++ -模块一 常量变量、关键字、数据类型】

C 模块一C框架代码&#xff1a;第一个C程序&#xff0c;打印hello C &#xff01;代码注释&#xff1a;一 变量和常量&#xff1a;1.1变量变量创建语法&#xff1a;1.2 常量&#xff1a;不能被修改的数据&#xff08;1&#xff09; #define定义的宏常量&#xff1a;一般写在文件…

排序(3)之交换排序

目录 前言 交换排序 1.冒泡排序 1.1冒泡排序的实现 1.2 特性总结 2.快速排序 2.1hoare版本 2.2 挖坑法 2.3 前后指针版本 3.快速排序的优化 3.1 三数取中法 3.2 小区间优化 4.快速排序的非递归实现 前言 今天小编给大家带来交换排序的内容&#xff0c;对于交换排序…

C-关键字(下)

文章目录循环控制switch-case-break-defaultdo-while-forgetchar()break-continuegotovoidvoid*returnconstconst修饰变量const修饰数组const修饰指针指针补充const 修饰返回值volatilestruct柔型数组union联合体联合体空间开辟问题利用联合体的性质,判断机器是大端还是小端enu…

力扣javascript刷题343——动态规划之整数拆分

这几天有在开始投暑期实习的简历&#xff0c;可能确实是投的太晚了&#xff0c;好多厂都没有hc了&#xff0c;阿里简历面都没过&#xff08;感觉是kpi面试&#xff09;&#xff0c;被深深打击到了呜呜呜&#xff0c;花了两天整理情绪&#xff0c;重新出发&#xff0c;下篇文章针…

mysql 索引详解

mysql 索引索引分类1. 普通索引和唯一索引2. 单列索引和组合索引3. 全文索引4&#xff0e;空间索引操作使用索引1. 在已有表中添加索引2. 删除索引索引是一个单独存储在磁盘上的数据库结构&#xff0c;使用索引可以快速找出在某个或多个列中有一特定值的行&#xff0c;提高查询…

【C语言 -结构体 结构体声明、定义、初始化、结构体成员访问、结构体传参】

C语言 - 结构体声明、定义、初始化、结构体成员访问、结构体传参一 结构体类型的声明&#xff1a;声明格式&#xff1a;二 结构体的定义并初始化2.1用结构体创建&#xff08;定义&#xff09;结构体变量&#xff08;对象&#xff09;的两种方式&#xff1a;&#xff08;1&#…

WebRTC 系列(三、点对点通话,H5、Android、iOS)

WebRTC 系列&#xff08;二、本地 demo&#xff0c;H5、Android、iOS&#xff09; 上一篇博客中&#xff0c;我已经展示了各端的本地 demo&#xff0c;大家应该知道 WebRTC 怎么用了。在本地 demo 中是用了一个 RemotePeerConnection 来模拟远端&#xff0c;可能理解起来还有点…

HTTP协议:当下最主流的应用层协议之一,你确定不了解一下吗?

一.HTTP协议的含义http是什么&#xff1f;超文本传输协议&#xff08;Hyper Text Transfer Protocol&#xff0c;HTTP&#xff09;是一个简单的请求-响应协议&#xff0c;它通常运行在TCP之上。‘超’可以理解为除了文本之外的图片&#xff0c;音频和视频&#xff0c;和一些其他…

硬盘、文件系统相关常识

1.硬盘 以机械硬盘为例&#xff0c;下面是机械硬盘的外形结构。 结构图&#xff1a; 每个磁盘分为两个盘面&#xff0c;每个盘面中有很多磁道(Disk Track)&#xff0c;每个磁道上有很多扇区(Sector)&#xff0c;磁道上的一段一段的就是扇区。 扇区是最小的单位&#xff0c;…

Flutter开发日常练习-黑白主题

1.添加了白天黑夜模式 2.country_picker: ^2.0.20 城市信息框架 3.image_picker: ^0.8.53 photo_manager: ^2.3.0 相机和相册的调用 4.shared_preferences: ^2.0.8 sqflite: ^1.3.1 path: 数据异步持久化到磁盘 注:登录的时候记录一下登录状态isLogin,通过isLogin来标记是否…

OCR之论文笔记TrOCR

文章目录TrOCR: Transformer-based Optical Character Recognition with Pre-trained Models一. 简介二. TrOCR2.1. Encoder2.2 Decoder2.3 Model Initialiaztion2.4 Task Pipeline2.5 Pre-training2.6 Fine-tuning2.7 Data Augmentation三. 实验3.1 Data3.2 Settings3.2 Resul…