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

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

在这里插入图片描述

前言

当我是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 {@Autowiredprivate OrderDao orderDao;// this method is used to query order by idpublic Order queryById(Long id) {// if optional.isPresent() is false, it will throw NoSuchElementExceptionreturn 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 {@Autowiredprivate OrderService orderService;@Testvoid 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 {@Testvoid 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 {@Mockprivate StringUtil1 util;@Testvoid 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 {@Mockprivate StringUtil3 mockUtil;@Testvoid 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风格写法

    @Testvoid 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()

例子:

    @Testvoid 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()
    @Testvoid testStubThrow2() {Mockito.doThrow(new UnsupportedOperationException()).when(mockUtil).formatString(Mockito.any(String.class));String str = null;assertThrows(UnsupportedOperationException.class, () ->  mockUtil.formatString("test"));}



given().willThrow()
    @Testvoid 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 {@Mockprivate 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*/@Testvoid 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个写法

   @Testvoid 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 风格

    @Testvoid 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 {@Mockprivate StringUtil5 mockUtil;@Testvoid 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()

另一种写法

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



given().willCallRealMethod()

BDD 风格

    @Testvoid 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 {@Mockprivate StringUtil3 mockUtil;@Testvoid 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 {@Spyprivate StringUtil8 spyUtil;@Testvoid testRealMethod() {StringUtil8 util = new StringUtil8();List<String> list = Arrays.asList("test", "test2");util.formatString(list);//assert the listList<String> expected = Arrays.asList("test: 20240101", "test2: 20240101");assertEquals(expected,list);}@Testvoid 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 listList<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 {@Testvoid 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)//Stubgiven(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//Stubgiven(b.....)assert(A....)
}

方法三 同过@InjectMocks 注解

@Mock
private B b;@InjectMocks
private A a@Test
testA(){//Stubgiven(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 {@Mockprivate Dependency1 dependency1;@Mockprivate Dependency2 dependency2;@InjectMocks(constructorArgs = {"dependency1", "dependency2"})private MyClass myClass;@Testpublic 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 {@Mockprivate OrderDao orderDao;@InjectMocksprivate OrderService orderService;private Order orderToTest;private Order orderDetails;@BeforeEachvoid beforeEach() {orderToTest = Order.builder().commodityName("ASUS ROG ZEPHYRUS").price(3L).userId(3L).build();orderDetails= Order.builder().commodityName("ASUS ROG ZEPHYRUS V3").price(4L).build();}@Testvoid updateOrder() {log.info("updateOrder test start");// ==== happy flow ==========================================================================// force the orderDao to return the orderToTestgiven(orderDao.findById(Mockito.anyLong())).willReturn(Optional.of(orderToTest));// let orderDao.save() successfully return the orderToTestgiven(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 workassertEquals(orderDetails.getCommodityName(), orderUpdated.getCommodityName());assertEquals(orderDetails.getPrice(), orderUpdated.getPrice());// ==== exception case 1 , order not found ============================================================================// clean all stubs for an objectMockito.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 对象的方法执行次数, 和执行时的参数等

个人并不常用

例子:
参考上面的例子

相关文章:

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

前言 当我是1个3年初级程序员时&#xff0c; 我被面试者问到1个问题&#xff1a; 如何保证你的开发任务交付质量 当我是1个7年开发组长时&#xff0c; 我被面试者问到另1个问题&#xff1a;如何保证你的团队的代码质量&#xff0c; 减少rework。 又若干年后&#xff0c; 我才…...

微服务sentinel解析部署使用全流程

sentinel源码地址&#xff1a; 介绍 alibaba/Sentinel Wiki GitHub sentinel官方文档&#xff1a; https://sentinelguard.io/zh-cn/docs/introduction.html Sprong Cloud alibaba Sentinel文档【小例子】 : Sentinel alibaba/spring-cloud-alibaba Wiki GitHub 目录 1、…...

YOLO11震撼发布!

非常高兴地向大家介绍 Ultralytics YOLO系列的新模型&#xff1a; YOLO11&#xff01; YOLO11 在以往 YOLO 模型基础上带来了一系列强大的功能和优化&#xff0c;使其速度更快、更准确、用途更广泛。主要改进包括 增强了特征提取功能&#xff0c;从而可以更精确地捕捉细节以更…...

机器学习框架(含实例说明)

机器学习框架是用于开发和部署机器学习模型的软件库和工具集。它们提供了一系列的算法、工具和基础设施&#xff0c;帮助开发者更高效地构建、训练和部署机器学习模型。以下是一些主要的机器学习框架及其详细介绍&#xff1a; 1. TensorFlow TensorFlow 是由Google开发的开源…...

vue2与vue3知识点

1.vue2&#xff08;optionsAPI&#xff09;选项式API 2.vue3&#xff08;composition API&#xff09;响应式API vue3 setup 中this是未定义&#xff08;undefined&#xff09;vue3中已经开始弱化this vue2通过this可以拿到vue3setup定义得值和方法 setup语法糖 ref > …...

从源码中学习动态代理模式

动态代理模式 动态代理是 Java 反射&#xff08;Reflection&#xff09;API 提供的一种强大机制&#xff0c;它允许在运行时创建对象的代理实例&#xff0c;而不需要在编译时静态地创建。 Java 提供了两种主要的方式来实现动态代理&#xff1a; 基于接口的动态代理&#xff1a…...

谷歌浏览器完美清除缓存

1.在页面上按下键盘的F12&#xff0c;打开控制台。 2.鼠标放到刷新图标上&#xff0c;点击鼠标右键&#xff0c;选择‘清空缓存并硬性重新加载’。 这样浏览器对网站页面的缓存就彻底被清理干净了。 目前支持该操作方式的浏览器有谷歌和Edge浏览器。 有的浏览器不支持该方式操…...

《如何高效学习》

有道云笔记 第一部分 整体性学习策略 结构 结构就像思想中的一座城市&#xff0c;有很多建筑物&#xff0c;建筑物之间有道路相连&#xff0c;有高大而重要的与其他建筑有上百条路相连&#xff0c;无关紧要的建筑只有少数泥泞的小道与外界相通。 建立良好的知识结构就是绘制…...

阿里云ACP认证考试题库

最近有好些同学&#xff0c;考完阿里云ACP了&#xff0c;再来跟我反馈&#xff1a;自己花700买的阿里云ACP题库&#xff0c;结果答案是错的&#xff01; 或者考完后发现&#xff0c;买的阿里云ACP题库覆盖率只有50%&#xff01; 为避免大家继续踩坑&#xff0c;给大家分享一个阿…...

学习经验分享【38】YOLOv11解读——最新YOLO版本

YOLO算法更新速度很快&#xff0c;已经出到V11版本&#xff0c;后续大家有想发论文或者搞项目可更新自己的baseline了。后续将改进YOLOv11算法&#xff0c;有需要的朋友可关注&#xff0c;我会持续进行更新。 YOLO11是Ultralytics YOLO系列实时目标检测器的最新迭代版本&#x…...

电商选品/分析| 亚马逊常见插件爬虫实战之-helium插件

说明 插件爬虫相当于二次爬虫,二次加工信息,因为大部分插件信息也是从正规网上去获取数据,这次列举helium插件爬虫案例,其他插件爬虫也是类似这个方式. 需求 1、⽤⾕歌浏览器&#xff0c;下载chrome extension&#xff1a;“Helium 10 2、登录helium10 3、打开 打开Amazo…...

遇到慢SQL、SQL报错,应如何快速定位问题 | OceanBase优化实践

在数据库的使用中&#xff0c;大家时常会遇到慢SQL&#xff0c;或执行出错的SQL。对于某些SQL问题&#xff0c;其错误原因显而易见&#xff0c;但也有不少情况难以直观判断。面对这类问题&#xff0c;我们应当如何应对&#xff1f;如何准确识别SQL错误的根源&#xff1f;是否需…...

postgresql僵尸进程的处理思路

简介 僵尸进程&#xff08;zombie process&#xff09;是指一个已经终止但仍然在进程表中保留条目的进程。正常情况下&#xff0c;当一个进程完成执行并退出时&#xff0c;操作系统会通过父进程调用的wait()或waitpid()系统调用来收集该子进程的退出状态。如果父进程未及时调用…...

Springboot 练习

Springboot练习——分页查询 Emp类 package com.wzb.pojo20240930;import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor;import java.time.LocalDate; import java.time.LocalDateTime;Data NoArgsConstructor AllArgsConstructor public…...

ISA-95制造业中企业和控制系统的集成的国际标准-(3)

ISA-95 文章目录 ISA-95ISA-95设备对象模型一、设备对象模型是什么&#xff1f;二、设备对象模型常见组织 ISA-95设备对象模型 ISA-95 标准中的设备对象模型侧重于表示制造和生产过程中使用的物理和逻辑设备及资源。 一、设备对象模型是什么&#xff1f; 设备对象模型提供了…...

MATLAB中图形导出功能的详细使用指南

在MATLAB中&#xff0c;图形的导出是一个常见的需求&#xff0c;无论是为了报告、演示还是进一步的分析。MATLAB提供了多种方式来导出图形&#xff0c;包括使用图形用户界面(GUI)的工具&#xff0c;以及通过编程方式使用特定的函数。本文将详细介绍如何在MATLAB中导出图形&…...

助农小程序|助农扶贫系统|基于java的助农扶贫系统小程序设计与实现(源码+数据库+文档)

助农扶贫系统小程序 目录 基于java的助农扶贫系统小程序设计与实现 一、前言 二、系统功能设计 三、系统实现 5.1.1 农户管理 5.1.2 用户管理 5.1.3 订单统计 5.2.1 商品信息管理 5.3.1 商品信息 5.3.2 订单信息 5.3.3 商品评价 5.3.4 商品退货 四、数据库设计 1、…...

SpringBoot上传图片实现本地存储以及实现直接上传阿里云OSS

一、本地上传 概念&#xff1a;将前端上传的文件保存到自己的电脑 作用&#xff1a;前端上传的文件到后端&#xff0c;后端存储的是一个临时文件&#xff0c;方法执行完毕会消失&#xff0c;把临时文件存储到本地硬盘中。 1、导入文件上传的依赖 <dependency><grou…...

git clone或repo init 时报错:fatal: 协议错误:错误的行长度 xxx

执行repo init或git clone时报错:protocol error: bad line length 或协议错误:错误的行长度 系统版本:Ubuntu20.04 repo version v2.47 repo launcher version 2.45 git version 2.25.1 报错信息 fatal: 协议错误:错误的行长度 948 fatal: 远端意外挂断了 repo: err…...

SpringBoot2(Spring Boot 的Web开发 springMVC 请求处理 参数绑定 常用注解 数据传递 文件上传)

SpringBoot2&#xff08;Spring Boot 的Web开发 springMVC 请求处理 参数绑定 常用注解 数据传递 文件上传&#xff09; 一、Spring Boot的Web开发 1.静态资源映射规则 总结&#xff1a;只要静态资源放在类路径下&#xff1a; called /static (or /public or /resources or …...

成都网安周暨CCS2024 | 大模型安全与产业应用创新研讨活动成功举办

9月11日-12日&#xff0c;作为2024年国家网络安全宣传周成都系列活动的重磅活动之一&#xff0c;CCS 2024成都网络安全系列活动在成都举行。“大模型安全与产业应用创新研讨活动”同期举办&#xff0c;本场活动由百度安全、成都无糖信息联合承办&#xff0c;特邀云安全联盟CSA大…...

React 解释常见的 hooks: useState / useRef / useContext / useReducer

前言 如果对 re-render 概念还不清楚&#xff0c;建议先看 React & 理解 re-render 的作用、概念&#xff0c;并提供详细的例子解释 再回头看本文。 如果对 React 基础语法还不熟练&#xff0c;建议先看 React & JSX 日常用法与基本原则 再回头看本文。 useState useS…...

telnet发送邮件教程:安全配置与操作指南?

telnet发送邮件的详细步骤&#xff1f;怎么用telnet命令发邮件&#xff1f; 尽管现代邮件客户端和服务器提供了丰富的功能和安全性保障&#xff0c;但在某些特定场景下&#xff0c;了解如何使用telnet发送邮件仍然是一项有价值的技能。AokSend将详细介绍如何安全配置和操作tel…...

超强大的 Nginx 可视化管理工具

今天给大家介绍一款 Nginx 可视化管理界面&#xff0c;非常好用&#xff0c;小白也能立马上手。 nginx-proxy-manager 是一个反向代理管理系统&#xff0c;它基于 NGINX&#xff0c;具有漂亮干净的 Web UI。还可以获得受信任的 SSL 证书&#xff0c;并通过单独的配置、自定义和…...

Android 安装应用-提交阶段之后剩下的操作

它的实现代码在executePostCommitSteps(commitRequest)中&#xff0c;看一下它的代码&#xff1a; /*** On successful install, executes remaining steps after commit completes and the package lock* is released. These are typically more expensive or require calls t…...

buuctf [ACTF2020 新生赛]Include

学习笔记。 开启靶机。 进入靶场&#xff1a; 我们跟进 tips瞅瞅&#xff1a; 额&#xff0c;纯小白&#xff0c;能想到的就是先F12看看&#xff0c;在CTRLu、以及抓包。 得&#xff0c;不会了&#xff0c;看wp呗&#xff0c;不会死磕没脑子0,0&#xff1f; 参考&#xff1a;…...

JS使用MutationObserver接口来监听DOM的更新

在JavaScript中&#xff0c;可以使用MutationObserver接口来监听DOM的更新。以下是一个使用MutationObserver的示例代码&#xff0c;它监听一个DOM节点的变化&#xff0c;并在变化发生时输出信息 // 选择目标节点 const targetNode document.getElementById(some-id);// 创建…...

图解C#高级教程(三):泛型

本讲用许多代码示例介绍了 C# 语言当中的泛型&#xff0c;主要包括泛型类、接口、结构、委托和方法。 文章目录 1. 为什么需要泛型&#xff1f;2. 泛型类的定义2.1 泛型类的定义2.2 使用泛型类创建变量和实例 3. 使用泛型类实现一个简单的栈3.1 类型参数的约束3.2 Where 子句3…...

240930_CycleGAN循环生成对抗网络

240930_CycleGAN循环生成对抗网络 CycleGAN&#xff0c;也算是笔者记录GAN生成对抗网络的第四篇&#xff0c;前三篇可以跳转 240925-GAN生成对抗网络-CSDN博客 240929-DCGAN生成漫画头像-CSDN博客 240929-CGAN条件生成对抗网络-CSDN博客 在第三篇中&#xff0c;我们采用了p…...

ide 使用技巧与插件推荐

ide 使用技巧与插件推荐 一、IDE 使用技巧 1. 快捷键 掌握常用快捷键&#xff1a; Windows: 使用 Ctrl、Alt 和 Shift 的组合。 Mac: 使用 Cmd、Option 和 Shift。 常用快捷键示例&#xff1a; VS Code: Ctrl P: 快速打开文件。 Ctrl Shift P: 打开命令面板。 Ctrl /…...

免费二级域名解析网站/公众号推广合作平台

Weaver博客地址更改通知 http://blog.sina.com.cn/weaver转载于:https://www.cnblogs.com/Weaver/archive/2009/07/12/1521854.html...

高端的环保行业网站开发/网络营销主要干什么

一个服务(service)通常指的是已知的接口或者抽象类&#xff0c;服务提供方就是对这个接口或者抽象类的实现&#xff0c;然后按spi标准存放到资源路径META-INF/services目录下&#xff0c;文件的命名为该服务接口的全限定名。如有一个服务接口com.test.Service&#xff0c;其服务…...

电子商务网站建设感想/专业seo推广

在C语言中&#xff0c;结构是一种复合数据类型&#xff0c;其构成元素既可以是基本数据类型&#xff08;如int、long、float等&#xff09;的变量&#xff0c;也可以是一些复合数据类型&#xff08;如数组、结构、联合等&#xff09;的数据单元。在结构中&#xff0c;编译器为结…...

wordpress建企业网站/最好的推广平台是什么软件

转自:http://blog.csdn.net/awj3584/article/details/16963525 Solr调研总结 开发类型 全文检索相关开发 Solr版本 4.2 文件内容 本文介绍solr的功能使用及相关注意事项;主要包括以下内容:环境搭建及调试;两个核心配置文件介绍;维护索引;查询索引,和在查询中可以应用的高亮…...

网页版梦幻西游谛听怎么获得/杭州网络排名优化

使用的环境&#xff1a;Xcode V8.3.3 学习OpenGL的过程中&#xff0c;会使用到gltools,glew,glfw3,glut等库文件&#xff0c;glut包Mac自带&#xff0c;故不需要考虑。主要考虑的是另三个包文件怎样安装&#xff0c;配置。本文主要讲两大部分&#xff1a; 1. glew&#xff0c;…...

行业网站作用/营销是做什么

docker的网络Docker 安装时会自动在host上创建三个网络&#xff0c;我们可用 docker network ls命令查看&#xff1a;[rootlocalhost ~]# docker network lsNETWORK ID NAME DRIVER SCOPE0164da7ee66a bridge bridge…...