Junit 5 - 理解Mockito,提高UT 覆盖率

news2024/9/28 9:02:23

在这里插入图片描述

前言

当我是1个3年初级程序员时, 我被面试者问到1个问题: 如何保证你的开发任务交付质量
当我是1个7年开发组长时, 我被面试者问到另1个问题:如何保证你的团队的代码质量, 减少rework。

又若干年后, 我才明白当年我的回答是多么的傻嗨, 什么理解业务, 勤沟通, 代码review流程都是废话。
真正的核心是测试! 足够的单元测试!

当你的下属提交pr时,顺便把 一份coverage 达到80% 的Junit testing report 放上jira, 你还不信任他这次任务的代码质量吗?

但是提高Junit 的覆盖率并不简单, 很多当前项目上的依赖, 这时, 我们就是需要Mockito了!



什么是Mockito

Mockito 是一个流行的 Java 单元测试框架,用于模拟和验证对象的行为。它提供了一组简单易用的 API,使得在编写单元测试时,可以轻松地创建和控制模拟对象,以便在测试中隔离依赖项和验证对象的行为。

Mockito 的主要特点包括:

简单易用的 API:Mockito 提供了一组简单易用的 API,使得在编写单元测试时,可以轻松地创建和控制模拟对象。
模拟任意对象的行为:Mockito 可以模拟任意对象的行为,包括静态方法、私有方法和 final 方法等。
验证对象的行为:Mockito 提供了一组 API 来验证对象的行为,例如验证方法是否被调用、验证方法的参数是否正确等。
支持多种测试框架:Mockito 可以与多种测试框架一起使用,如 JUnit、TestNG 等。
强大的功能:Mockito 提供了一些强大的功能,如模拟方法的返回值、模拟方法的异常抛出、模拟方法的调用次数等。
Mockito 广泛应用于 Java 开发中,被广泛认为是 Java 单元测试中最流行和最强大的框架之一。它提供了一种简单而强大的方式来编写单元测试,并且在测试中隔离依赖项和验证对象的行为。



为什么需要Mockito

简单来讲 Mockito 可以模拟1个类的对象, 并强制指定or 改变这个对象的行为

例如, 类A的方法a() 返回 当前日期的String “20241001”
但是 我们可以mock 1个对象 aMock , 并指定aMock.a() 返回 “20000101”
这样, 当我们的测试调用了aMock.a() , 它真的会返回"20000101"

这不是脱裤子放屁吗?

实际上, 既然我们mock了A类, 我们想要测试的并不是A类的代码, 而是另个调用了A类的类的代码 (B 具有1个A对象的成员)

例如 类B 的b() 方法 调用了 aObj.a() (aObj 是 类A的对象)

当我们测试类B() 时, 可以mock掉A, 并随时改变a() 方法的行为, 并测试不同a()返回值下 , B的行为是否正确



一个具体场景

上面还是说的太绕了

举个例子
例如我有1个Service 类 OrderService:
OrderService.java

@Service
@Slf4j
public class OrderService {

    @Autowired
    private OrderDao orderDao;

    // this method is used to query order by id
    public Order queryById(Long id) {
        // if optional.isPresent() is false, it will throw NoSuchElementException
        return orderDao.findById(id).orElseThrow(() -> new NoSuchElementException("No such order by id: " + id));
    }

    public Order updateOrder(Long orderId, Order orderDetails) {

        Order order = null;
        try{
            order = this.queryById(orderId);
        } catch (NoSuchElementException e) {
            log.error("Error in getting order by id...", e);
            throw e;
        } catch (QueryTimeoutException e) {
            log.error("timeout..", e);
            return null;
        }

        order.setCommodityName(orderDetails.getCommodityName());
        order.setPrice(orderDetails.getPrice());

        return orderDao.save(order);
    }

    public Order createOrder(Order order) {
        return orderDao.save(order);
    }

    public void deleteOrder(Long orderId) {
        orderDao.deleteById(orderId);
    }
}

其中该类调用了Dao类 OrderDao, 而OrderDao是直接与数据交互的

如果我们正常编写UT, 与数据真是交互, 编写1个for updateOrder()的测试方法

OrderServiceIntrusiveTest.java

@SpringBootTest
@ActiveProfiles("dev")
class OrderServiceIntrusiveTest {

    @Autowired
    private OrderService orderService;

    @Test
    void updateOrder() {

        Order order = Order.builder().commodityName("ASUS ROG ZEPHYRUS").price(3L).userId(3L).build();
        order = orderService.createOrder(order);

        Order orderUpdated = Order.builder().commodityName("ASUS ROG ZEPHYRUS V3").price(4L).build();
        orderUpdated = orderService.updateOrder(order.getId(), orderUpdated);
        assertEquals("ASUS ROG ZEPHYRUS V3", orderUpdated.getCommodityName());
        assertEquals(4L, orderUpdated.getPrice());
        
        orderService.deleteOrder(order.getId());
    }
}

这个case 能正常执行

但是有下面若干缺点:

  1. 依赖dev env的db 环境, 如果dev env 出问题, 测试就failed了
  2. 必须启动springboot 容器, 执行时间偏长, 特别是多个测试类的情况下
  3. 为了让测试可重复执行, 测试完之后必须清理数据
  4. 覆盖率低, 难以cover exception 处理场景
    在这里插入图片描述
    上红色条标记的代码都是未有cover的

至于怎么测试 Timeout 场景下业务代码? 难道还要测试中拔网线吗?
如果Exception 场景无法测试, 只测试了happyflow, 如何让你的技术经理放心的你的交付呢?

而Mockito 可以解决这些问题.



构建Mock对象

从这里开始我们拆解Mockito的各个方法使用要点

首先引入mockito 依赖

 <!-- mockito -->
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-core</artifactId>
            <version>5.7.0</version>
            <scope>test</scope>
        </dependency>



使用Mockito.mock()

public class BuildMockObjTest {

    @Test
    void testBuildMockObj() {
        StringUtil1 util = Mockito.mock(StringUtil1.class);
        String str = util.formatString("test");
        assertNull(str); // should be null
    }
}


class StringUtil1 {
    public String formatString(String str) {
        return str + ": " + LocalDate.now().toString();
    }
}

这例子中, 我们先写了1个 StringUtil1类, 它有1个方法可以为1个String 添加日期suffix

我们可以用Mockito.mock方法构建1个mock 的StringUtil 对象, 这个mock对象创建后里面的所有方法都是未知空位, 等待我们的指定
所以当我们直接调用 String str = util.formatString(“test”); 时, 其实并没有调用真正 formatString()里的代码, 返回的是null



使用@Mock 注解

下面写法与上面是等价的
在项目中使用注解更加常见
注意的事测试类本身需要 @ExtendWith(MockitoExtension.class) 注解

@ExtendWith(MockitoExtension.class)
public class BuildMockObjWithAnnotationTest {

    @Mock
    private StringUtil1 util;

    @Test
    void testBuildMockObj() {
        String str = util.formatString("test");
        assertNull(str); // should be null
    }
}



打桩 (Stub)



什么是打桩Stub与断言

先说下什么是打桩

Mockito 的核心 概念就是打桩和断言, 其中打桩是Mockito 特有的行为
简单来讲打桩就是指定 mock 对象的方法行为

举个例子

我令 dao 方法正常返回, 那么service 的update() 方法输出1
我令 dao 方法timeout, 那么service 的update()应该10秒后爆出异常

上面的令 某个mock方法 执行某种行为的本身 就是打桩, 后面的那么 就是断言, 如果update()的行为并不是断言描述的情况, 我们认为测试不通过, 需要开发人员检查业务代码。

断言就是所谓的Assert了, 这个与mockito没关, 任何UT 都应该包含至少一个断言



Stub 修改方法的返回逻辑



when().thenReturn()

thenReturn 可以直接修改1个对象方法的输出逻辑
例如:

@ExtendWith(MockitoExtension.class)
@Slf4j
public class StubTest {

    @Mock
    private StringUtil3 mockUtil;
    
    @Test
    void testStubReturn() {
        Mockito.when(mockUtil.formatString(Mockito.any(String.class))).thenReturn("test: " + LocalDateTime.now().toString());
        String str = mockUtil.formatString("test");
        log.info("str: {}", str);
        assertNotNull(str);
    }

}

class StringUtil3 {
    
    public String formatString(String str) {
        return str + ": " + LocalDate.now().toString();
    }
}

原本方法是 输出 参数 + 日期, 但是thenReturn 直接修改为 “test" + 日期时间, 注意这里的test 是hardcode的
输出:

10:13:31.827 [main] INFO com.home.javacommon.mockito.StubTest -- str: test: 2024-09-28T10:13:31.816733893



DoReturn().when()

这是另1风格写法

    @Test
    void testStubReturn2() {
        Mockito.doReturn("test: " + LocalDateTime.now().toString()).when(mockUtil).formatString(Mockito.any(String.class));
        String str = mockUtil.formatString("test");
        log.info("str: {}", str);
        assertNotNull(str);
    }

注意这里when() 后值, 而when()参数是1个mock对象, 而不是mork对象带方法



given().willReturn()

这里是BDD 写法, 我更prefer 这种

    void testStubReturn3() {
        given(mockUtil.formatString(Mockito.any(String.class))).willReturn("test: " + LocalDateTime.now().toString());
        String str = mockUtil.formatString("test");
        log.info("str: {}", str);
        assertNotNull(str);
    }

效果也是一样的



Stub 令对象方法抛出异常



when().thenThrow()

例子:

    @Test
    void testStubThrow() {
        Mockito.when(mockUtil.formatString(Mockito.any(String.class))).thenThrow(new UnsupportedOperationException());
        String str = null;
        assertThrows(UnsupportedOperationException.class, () ->  mockUtil.formatString("test"));
    }

值得注意是, Mock对象的方法定义如果没有throws 1个 CheckedException的话, 不能打桩让Mock对象抛出这个CheckException的, 这里的例子是让其抛出1个RuntimeException



doThrow().when()
    @Test
    void testStubThrow2() {
        Mockito.doThrow(new UnsupportedOperationException()).when(mockUtil).formatString(Mockito.any(String.class));
        String str = null;
        assertThrows(UnsupportedOperationException.class, () ->  mockUtil.formatString("test"));
    }



given().willThrow()
    @Test
    void testStubThrow3() {
        given(mockUtil.formatString(Mockito.any(String.class))).willThrow(new UnsupportedOperationException());
        String str = null;
        assertThrows(UnsupportedOperationException.class, () ->  mockUtil.formatString("test"));
    }



Stub 灵活地令对象方法执行指定行为

例如, 如果我想让方法返回其第1和第2个参数的合并字符串

而上面的Return 方式是无法获取参数的值的



when().thenAnswer()

例子:

@ExtendWith(MockitoExtension.class)
@Slf4j
public class StubAnswerTest {

    @Mock
    private StringUtil4 mockUtil;

    /*
     *  invocation is a concept of the mockito invocation, it includes the context information of the mock
     *  e.g. the argument of the method,  return values
     *
     *  usually, you can use the argument(0) to get the first argument
     *
     *  some methods
     *  getArgument(int index): get the argument at index
     *  getArguments(): get all arguments
     *  getMethod(): get the method
     *  getMock(): get the mock object
     */
    @Test
    void testStubAnswer() {
        Mockito.when(mockUtil.formatString(Mockito.any(String.class), Mockito.any(String.class)))
                .thenAnswer((invocation) -> invocation.getArgument(0) + ":" + invocation.getArgument(1));
        String str = mockUtil.formatString("test", "test2");
        assertEquals("test:test2", str);
    }
}

class StringUtil4 {

    public String formatString(String str1, String str2) {
        return "it's hard coded";
    }
}

值得注意的是 invocation的使用, 参考上面的注解



doAnswer().when()

另1个写法

   @Test
    void testStubAnswer2() {
        Mockito.doAnswer((invocation) -> invocation.getArgument(0) + ":" + invocation.getArgument(1))
                .when(mockUtil).formatString(Mockito.any(String.class), Mockito.any(String.class));
        String str = mockUtil.formatString("test", "test2");
        assertEquals("test:test2", str);
    }



given().willAnswer()

BDD 风格

    @Test
    void testStubAnswer3() {
        given(mockUtil.formatString(Mockito.any(String.class), Mockito.any(String.class)))
                .willAnswer((invocation) -> invocation.getArgument(0) + ":" + invocation.getArgument(1));
        String str = mockUtil.formatString("test", "test2");
        assertEquals("test:test2", str);
    }



Stub 调用真实的方法

有一种场景

1个Mock 对象里有若干个方法, 但是Mock出来后, 所有的方法的行为都是空的 , 但是Mockito 提供了CallRealMethod 功能, 让我们可以让其中某个(or 若干个) 方法使用真实定义的逻辑



when().thenCallRealMethod()

下面的例子, Mock对象里有两个方法
而我们只打桩了 第2个让其执行真实定义的代码

所以第一个方法输出是null
第2个方法正常输出

@ExtendWith(MockitoExtension.class)
public class StubCallRealMethodTest {

    @Mock
    private StringUtil5 mockUtil;


    @Test
    void testStubCallRealMethod() {
        Mockito.when(mockUtil.formatString(Mockito.any(String.class), Mockito.any(String.class))).thenCallRealMethod();
        String str = mockUtil.formatString("test");
        assertNull(str);

        str = mockUtil.formatString("test", "test2");
        assertEquals("test:test2", str);
    }
}


class StringUtil5 {

    public String formatString(String str) {
        return str + ": " + LocalDate.now().toString();
    }

    public String formatString(String str1, String str2) {
        return str1 + ":" + str2;
    }
}



DoCallRealMethod().when()

另一种写法

    @Test
    void testStubCallRealMethod2() {
        Mockito.doCallRealMethod().when(mockUtil).formatString(Mockito.any(String.class), Mockito.any(String.class));
        String str = mockUtil.formatString("test");
        assertNull(str);
    }



given().willCallRealMethod()

BDD 风格

    @Test
    void testStubCallRealMethod3() {
        given(mockUtil.formatString(Mockito.any(String.class), Mockito.any(String.class))).willCallRealMethod();
        String str = mockUtil.formatString("test");
        assertNull(str);

        str = mockUtil.formatString("test", "test2");
        assertEquals("test:test2", str);
    }



Stub 让无返回值 void 对象方法不抛出任何异常, DoNothing

如果只是让 方法不抛出异常 , 例如Mock 1个 Dao对象的 void sqlExec() 方法。
实际上就是让它 不做任何事情

而 when().then() 和 given().will() 都不适用于 mock1个无返回值方法的

这时我们只能用DoNothing



doNothing().when()

例子:

@ExtendWith(MockitoExtension.class)
@Slf4j
public class StubTest {

    @Mock
    private StringUtil3 mockUtil;

    @Test
    void testStubNotThrow() {
        Mockito.doNothing().when(mockUtil).formatString2(Mockito.any(String.class));
        assertDoesNotThrow(() -> mockUtil.formatString2("test"));
    }

}

class StringUtil3 {

    public void formatString2(String str) {
        throw new UnsupportedOperationException();
    }
}





Spy - Mock 1个"真实" 对象

有1个场景

1个类里定义了n个方法
我只想让定义某个方法的行为, 但是要让其他方法正常执行

当然我们可以Mock 这个对象后, 把其他的方法都打桩为CallRealMethod, 但是这个方法很蠢

更好的方法是Spy 1个对象



Mockito Spy 对象定义

什么是Spy?
在Mockito中,spy是一种特殊类型的mock对象,它可以部分模拟一个真实对象。
与普通的mock对象不同,spy对象保留了被模拟对象的真实行为,除非显式进行了模拟。
Spy对象的特点:
Spy对象会保留被模拟对象的原始行为,除非显式指定了模拟行为。
通过spy创建的对象是真实对象的一个代理,可以使用Mockito的方法来验证其行为。
可以通过spy对象来监视真实对象的方法调用,并可以选择性地进行模拟。
Spy的使用场景:
当您想要部分模拟一个真实对象,同时保留其原始行为时,可以使用spy对象。
适用于需要对对象的部分方法进行跟踪或验证的情况,而不需要完全模拟整个对象。



构建1个Spy 对象



方法一, 我们可以用Mockito.spy() 来创建

例如:

	SpyUtil = Mockito.spy(StringUtil.class)
方法二, 我们可以用@Spy 来定义, 同样需要@ExtendWith(MockitoExtension.class)

例如

@ExtendWith(MockitoExtension.class)
class xxx

@Spy
private StringUtil spyUtil;



spy的打桩

其实spy的打桩与mock对象的打桩无任何区别, 只不过mock对象的方法默认不打桩的话就是不执行任何代码
而spy 则相反, 不打桩的话会执行真实代码

例子:
下面的Spy对象有两个方法, 其中1个方法循环调用另1个方法

而这里只Stub了第2个方法, 让外部方法正常执行循环

@ExtendWith(MockitoExtension.class)
public class SpyTest {

    @Spy
    private StringUtil8 spyUtil;


    @Test
    void testRealMethod() {

        StringUtil8 util = new StringUtil8();
        List<String> list = Arrays.asList("test", "test2");
        util.formatString(list);
        //assert the list
        List<String> expected = Arrays.asList("test: 20240101", "test2: 20240101");
        assertEquals(expected,list);
    }

    @Test
    void testSpyMethod() {

        List<String> list = Arrays.asList("test", "test2");
        given(spyUtil.formatString(Mockito.any(String.class)))
                .willAnswer((invocation) -> invocation.getArgument(0) + ": " + "20241111");
        spyUtil.formatString(list);
        //assert the list
        List<String> expected = Arrays.asList("test: 20241111", "test2: 20241111");
        assertEquals(expected,list);
    }


}

@Slf4j
class StringUtil8 {
    
    public void formatString(List<String> list) {
        list.replaceAll(this::formatString);

        log.info("list: {}", list);
    }

    public String formatString(String str) {
        return str + ": " + "20240101";
    }
}



Mock 1个静态方法 – MockStatic

上面的例子都是Mock 1个对象内的方法。 而且Mock内的对象对真实其他对象的相同方法是无影响的, 它们分布在不同的heap 内存

问题来了,

如果1个静态方法可以被mock吗? 因为1个类的静态方法是共享的, 即使这个类创建了多个对象。

Mocktio.core 早期并不支持Mock 静态方法, 但是现在可以了

例子:

@ExtendWith(MockitoExtension.class)
public class MockitoStaticTest {

    @Test
    void testMockitoStatic() {

        try (MockedStatic<StringUtil9> mockStaticObj = Mockito.mockStatic(StringUtil9.class)) {
            given(StringUtil9.formatString(Mockito.any(String.class))).willReturn("test: " + "20241111");
            assertEquals("test: " + "20241111", StringUtil9.formatString("test"));
        } // try will auto execute the mockStaticObj.close()
        

        assertEquals("test: " + "20000101", StringUtil9.formatString("test"));

    }
}


class StringUtil9 {

    public static String formatString(String str) {
        return str + ": " + "20000101";
    }
}

注意, 强烈建议使用try with resource block 来使用 mockstatic , 否则你需要手动close mockobj, otherwise 程序不知道mock 什么时候结束



Inject 1个Mock 对象到另1个真实对象

大部分我们要面对的场景是:

我们要测试A类, A有1个B类成员 b, B类的方法对环境有依赖
所以我们要mock 1个B对象, 然后让B对象成为A测试对象的1个成员

方法有多种

方法一 通过构造函数inject

@Mock
private B b;

@Test
testA(){
	A a = new A(b)
	//Stub
	given(b.....)
	assert(A....)
}

但是很多时候B 是通过反射注入的(Spring)
A并没有1个有参的构造函数

方法二 同过反射注入Mock - ReflectionTestUtils

 @Mock
private B b;

@Test
testA(){
	A a = new A()
    ReflectionTestUtils.setField(a, "b", b); // “b" is the properties name in class A
	//Stub
	given(b.....)
	assert(A....)
}

方法三 同过@InjectMocks 注解

@Mock
private B b;

@InjectMocks
private A a

@Test
testA(){
	//Stub
	given(b.....)
	assert(A....)
}

项目更多地用这种写法

注意:

  1. InjectMocks 还是基于反射注入Mock 对象
  2. 会把所有用@Mock or@Spy 定义的mock 对象注入到@InjectMocks的对象中
  3. InjectMock 默认会使用类A 的无参函数来构建A对象a, 如果A没有 无参函数,有可能会有异常
  4. 可以使用 @InjectMocks(constructorArgs = {“dependency1”, “dependency2”}) 来指定使用某个有参函数

例子:

@RunWith(MockitoJUnitRunner.class)
public class MockitoInjectMocksTest {

    @Mock
    private Dependency1 dependency1;

    @Mock
    private Dependency2 dependency2;

    @InjectMocks(constructorArgs = {"dependency1", "dependency2"})
    private MyClass myClass;

    @Test
    public void testMyClass() {
        // ...
    }

    public static class MyClass {
        private final Dependency1 dependency1;
        private final Dependency2 dependency2;

        public MyClass(Dependency1 dependency1, Dependency2 dependency2) {
            this.dependency1 = dependency1;
            this.dependency2 = dependency2;
        }

        // ...
    }

    public interface Dependency1 {
        // ...
    }

    public interface Dependency2 {
        // ...
    }
}



用Mockito去解决本文开始提供的场景问题

很明显
上面的A类就是 场景下的Service 类, B类就是Dao 类, Dao类具有DB 的依赖!

重写后的test 方法:

@Slf4j
@ExtendWith(MockitoExtension.class)
class OrderServiceBddTest {

    @Mock
    private OrderDao orderDao;

    @InjectMocks
    private OrderService orderService;

    private Order orderToTest;

    private Order orderDetails;

    @BeforeEach
    void beforeEach() {
        orderToTest = Order.builder().commodityName("ASUS ROG ZEPHYRUS").price(3L).userId(3L).build();
        orderDetails= Order.builder().commodityName("ASUS ROG ZEPHYRUS V3").price(4L).build();
    }

    @Test
    void updateOrder() {
        log.info("updateOrder test start");


        // ==== happy flow ==========================================================================
        // force the orderDao to return the orderToTest
        given(orderDao.findById(Mockito.anyLong())).willReturn(Optional.of(orderToTest));

        // let orderDao.save() successfully return the orderToTest
        given(orderDao.save(any(Order.class))).willAnswer(invocation -> invocation.getArgument(0));


        Order orderUpdated = orderService.updateOrder(101L, orderDetails);
        Mockito.verify(orderDao).findById(101L);
        //Mockito.verify(orderService).queryById(101L); // not work
        assertEquals(orderDetails.getCommodityName(), orderUpdated.getCommodityName());
        assertEquals(orderDetails.getPrice(), orderUpdated.getPrice());


        // ==== exception case 1 , order not found ============================================================================

        // clean all stubs for an object
        Mockito.reset(orderDao);

        given(orderDao.findById(Mockito.anyLong())).willThrow(NoSuchElementException.class);

        assertThrows(NoSuchElementException.class, () -> orderService.updateOrder(101L, orderDetails));

        // ==== exception case 1 , order not found ============================================================================
        Mockito.reset(orderDao);
        given(orderDao.findById(Mockito.anyLong())).willThrow(QueryTimeoutException.class);

        assertDoesNotThrow(() -> orderService.updateOrder(101L, orderDetails));
        Mockito.verify(orderDao).findById(101L);
        assertNull(orderService.updateOrder(101L, orderDetails));
    }
}

可以见到, 使用mockito 我们很容易模拟db timeout 和 没有数据的问题, 真正地令覆盖率达到100%

Mockito 的verify

我认为verify 也是断言(Assert)的一种, 一种特殊的断言, 只能用于mock 对象上
verify 可以用于检查mock 对象的方法执行次数, 和执行时的参数等

个人并不常用

例子:
参考上面的例子

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

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

相关文章

代码随想录Day 58|拓扑排序、dijkstra算法精讲,题目:软件构建、参加科学大会

提示&#xff1a;DDU&#xff0c;供自己复习使用。欢迎大家前来讨论~ 文章目录 图论part08**拓扑排序精讲**题目&#xff1a;117. 软件构建拓扑排序的背景解题思路&#xff1a;模拟过程 **dijkstra&#xff08;朴素版&#xff09;精讲**题目&#xff1a;47. 参加科学大会解题思…

leetcode每日一题day14(24.9.24)——字符串最多的子序列

思路:对于应该首要和贡献联系起来&#xff0c;对于什么是贡献呢&#xff0c;即在某种情况下&#xff0c;会贡献出多少种&#xff0c;符合要求的结果&#xff0c;此题属于较为基础的&#xff0c;对于text中的一个字符如果是非pattern中的元素&#xff0c;则对结果无任何影响&…

舒舒活动图片 小暑至,暑气湿气缠身怎么办?中医教你消暑宁心,健脾祛湿

小暑标志着酷夏的到来&#xff0c;闷热潮湿的气候令人不适&#xff0c;仿佛暑湿交织笼罩全身。众多友人诉苦不已&#xff0c;此般环境致使心绪不宁、身躯疲惫&#xff0c;失措寻对策。实则无需忧虑&#xff0c;持之以恒地进行消暑养心、健脾除湿&#xff0c;便能安然度夏。 暑气…

Vue 技术入门 day1 模版语法、数据绑定、事件处理、计算属性与监视、class和style绑定、条件渲染v-if/v-show、列表渲染v-for

目录 1.Vue 核心 1.1. Vue 简介 1.1.1 介绍与描述 1.1.2 Vue 的特点 1.2 模板语法 1.2.1 模板的分类 1.2.2 插值语法 1.2.3 指令语法 1.2.4 实例 1.3 数据绑定 1.3.1 单向数据绑定 1.3.2 双向数据绑定 1.3.3 MVVM 模型 1.3.4 data与el的2种写法 1.3.5 实例 1.3.…

【C++前缀和】2731. 移动机器人|1922

本文涉及的基础知识点 C算法&#xff1a;前缀和、前缀乘积、前缀异或的原理、源码及测试用例 包括课程视频 LeetCode2731. 移动机器人 有一些机器人分布在一条无限长的数轴上&#xff0c;他们初始坐标用一个下标从 0 开始的整数数组 nums 表示。当你给机器人下达命令时&…

manim中实现文字换行和设置字体格式

实现换行 from manim import * class Textline(Scene): def construct(self): self.camera.background_color "#2F4F14" # 创建中心文本 horizontal_line Line(startLEFT * 8, endRIGHT * 8, colorWHITE).shift(3 * UP) stext Text("线性代数", …

0. Pixel3 在Ubuntu22下Android12源码拉取 + 编译

0. Pixel3 在Ubuntu22下Android12源码拉取 编译 原文地址: http://www.androidcrack.com/index.php/archives/3/ 1. 前言 这是一个非常悲伤的故事, 因为一个意外, 不小心把之前镜像的源码搞坏了. 也没做版本管理,恢复不了了. 那么只能说是重新做一次. 再者以前的镜像太老旧…

828华为云征文|部署去中心化网络的 AI 照片管理应用 PhotoPrism

828华为云征文&#xff5c;部署去中心化网络的 AI 照片管理应用 PhotoPrism 一、Flexus云服务器X实例介绍二、Flexus云服务器X实例配置2.1 重置密码2.2 服务器连接2.3 安全组配置2.4 Docker 环境搭建 三、Flexus云服务器X实例部署 PhotoPrism3.1 PhotoPrism 介绍3.2 PhotoPrism…

OpenHarmony标准系统上实现对rk系列芯片NPU的支持(驱动移植)

1.将RKNPU驱动移植到Openharmony内核 本文以rk3568为例&#xff0c;将RKNPU驱动移植到Openharmony使用的kernel 5.10中 开发环境 DAYU200 rk3568开发板OpenHarmony 4.1 Release 64位系统 文档约定&#xff1a;4.1r_3568为OpenHarmony标准系统源码根目录 1.0 环境准备 1.搭建O…

Windows11 + Ubuntu 24.10

我在win11安装Ubuntu主板:华硕主板Z790 DARK HERO,进入安装,所以文章中bios系统设置为华硕的bios系统。 一、确认系统信息-BIOS为UEFL 备注:UEFL优于MBR,具体可以查询问ai。如果BIOS模式中不是UEFL,建议为UEFL 1、 win+R 输入 msinfo32,打开系统信息,可以看到…

printf详解

printf("hello \nworld\n")&#xff1a;将hello word打印到屏幕上&#xff0c;在使用printf函数时可以多次使用换行符\n&#xff0c;想在哪里加都可以 int main() {printf("hello \nworld\n");return 0; } 占位符&#xff1a;在printf中&#xff0c;占位…

delphi制作漂亮的农历窗体(IntraWeb+Layui的完美结合)

delphi制作漂亮的农历窗体&#xff08;IntraWebLayui的完美结合&#xff09; 不需要安装服务器&#xff0c;Apache和IIS都不需要&#xff0c;自带企业级服务器。 运行exe服务器就架好了&#xff0c;直接打开手机浏览器或者电脑浏览器&#xff0c;网页就出来了&#xff0c;如果…

AI驱动TDSQL-C Serverless 数据库技术实战营-颠覆传统分析模式:智能体与TDSQL-C结合实现人才的可视化数据分析

文章目录 前言云数据库的对比传统云数据库&#xff1a;TDSQL-C Serverless: 智能体与TDSQL-C的结合思路 算力服务器与数据库服务器申请与部署购买 TDSQL-C Mysql Serverless 实例购买HAI高算力服务器 准备工作准备数据下载依赖 案例开发创建数据库开启智能体与TDSQL-C结合 总结…

智能新宠:BabyAlpha A2开启家庭机器人新时代

具身智能领域的“疯狂”&#xff0c;已经迈入了全新的阶段&#xff01;让我们一起来看看这段视频&#xff1a;一个人形机器人在前面奔跑&#xff0c;一群机器狗紧随其后&#xff1b;接着是人追赶机器狗&#xff0c;随后机器狗又追逐人……视频最后&#xff0c;那个机器人似乎还…

【Python】Daphne:Django 异步服务的桥梁

Daphne 是 Django Channels 项目的一部分&#xff0c;专门用于为 Django 提供支持 HTTP、WebSocket、HTTP2 和 ASGI 协议的异步服务器。Daphne 是一个开源的 Python 异步服务器&#xff0c;它可以帮助开发者运行异步应用程序&#xff0c;并且非常适合与 Django Channels 一起使…

回家啦回家啦

耒阳也有茶颜月色了&#xff0c;没忍住喝了一杯&#xff01; 衡阳卤粉&#xff0c;想出来的味道&#x1f445;&#xff0c;一般般 但一个粉店能做到24小时不打烊&#xff0c;应该也还行哈 银行竟然支持扫脸取钱了&#xff01;&#xff01;

【微服务即时通讯系统】——etcd一致性键值存储系统、etcd的介绍、etcd的安装、etcd使用和功能测试

文章目录 etcd1. etcd的介绍1.1 etcd的概念 2. etcd的安装2.1 安装etcd2.2 安装etcd客户端C/C开发库 3. etcd使用3.1 etcd接口介绍 4. etcd使用测试4.1 原生接口使用测试4.2 封装etcd使用测试 etcd 1. etcd的介绍 1.1 etcd的概念 Etcd 是一个基于GO实现的 分布式、高可用、一致…

通过OpenScada在ARMxy边缘计算网关上实现数字化转型

随着工业4.0概念的普及&#xff0c;数字化转型已成为制造业升级的关键路径之一。在此背景下&#xff0c;边缘计算技术因其能够有效处理大量数据、减少延迟并提高系统响应速度而受到广泛关注。ARMxy边缘计算网关&#xff0c;特别是BL340系列&#xff0c;凭借其强大的性能和灵活的…

--杂项2--

将之前实现的顺序表、栈、队列都更改成模板类 #include <iostream> #include <string.h> using namespace std;template <typename T> class Stack { private:T* a;int top;int size1;public:Stack(int c) : a(new T[c]), top(-1), size1(c) {}~Stack() { de…

IDEA 系列产品 下载

准备工作 下载 下载链接&#xff1a;https://www.123865.com/ps/EF7OTd-mbHnH 仅供参考 环境 演示环境&#xff1a; 操作系统&#xff1a;windows10 产品&#xff1a;IntelliJ IDEA 版本&#xff1a;2024.1.2 注意&#xff1a;如果需要其他产品或者版本可以自行下载&#xff0…