单元测试理论储备及JUnit5实战

news2024/11/15 21:36:59

概述

测试驱动开发,TDD,Test Driven Development,优点:

  1. 使得开发人员对即将编写的软件任务具有更清晰的认识,使得他们在思考如何编写代码之前先仔细思考如何设计软件
  2. 对测试开发人员所实现的代码提供快速和自动化的支持;
  3. 提供一系列可以重用的回归测试用例(regression test case),这些测试用例可以用来检测未来添加的新代码是否改变以前系统定义的行为(测试代码兼容性)。

TDD 也有局限性。测试代码的表达的局限性,有限的测试代码根本不能覆盖所有的代码行为。

理论Theory机制

理论的出现就是为了解决 TDD 这个问题。TDD 为组织规划开发流程提供一个方法,先用一些具体的例子(测试用例 test case)来描述系统代码的行为,然后再将这些行为用代码语句进行概括性的总的陈述(代码实现 implementation)。而 Theory 就是对传统的 TDD 进行一个延伸和扩展,它使得开发人员从开始的定义测试用例的阶段就可以通过参数集(理论上是无限个参数)对代码行为进行概括性的总的陈述,我们叫这些陈述为理论。理论就是对那些需要无穷个测试用例才能正确描述的代码行为的概括性陈述。结合理论和测试一起,可以轻松的描述代码的行为并发现 BUG 。开发人员都知道他们代码所想要实现的概括性的总的目的,理论使得他们只需要在一个地方就可以快速的指定这些目的,而不要将这些目的翻译成大量的独立的测试用例。

理论机制的优点:

  1. 理论使得开发完全抽象的接口更加容易。
  2. 理论仍然可以重用以前的测试用例,因为以前的许多传统的具体的测试用例仍然可以被轻松的改写成理论测试实例。
  3. 理论可以测试出一些原本测试用例没测出来的 bugs 。
  4. 理论允许配合自动化测试工具进行使用,自动化工具通过大量的数据点来测试一个理论,从而可以放大增强理论的效果。利用自动化工具来分析代码,找出可以证明理论错误的值。

Junit5

JUnit 5完全使用Java 8重写所有代码,JUnit 5允许在断言中使用Lambda表达式,可以从断言库AssertJ中可以看到。与JUnit 4不同,JUnit 5不再是单个库,而是模块化结构的集合,包括:自己的模块、引擎、launcher、针对Gradle和Surefire的集成模块JUnit团队还发起名为Open Test Alliance for the JVM的活动,OpenTest4j。JUnit 5还提供全新的一套注解集合,断言方法从JUnit 4的org.junit.Assert包移到JUnit 5的org.junit.gen5.api.Assertions包。

JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage

  • JUnit Platform是在JVM上启动测试框架的基础
  • JUnit Jupiter是JUnit5扩展的新的编程模型和扩展模型,用来编写测试用例。Jupiter子项目为在平台上运行Jupiter的测试提供了一个TestEngine (测试引擎)
  • JUnit Vintage提供一个在平台上运行JUnit 3和JUnit 4的TestEngine
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter</artifactId>
    <scope>test</scope>
</dependency>

实际上,在maven搜索junit时,如果要使用junit5,会指引使用新的artifactId:

Note: This artifact was moved to: 
org.junit.jupiter » junit-jupiter-api

不过,可以直接使用下面这个aggregator:
在这里插入图片描述
打开junit-jupiter的pom文件,也知道junit-jupiter是三个artifactId的汇总:

<dependency>
  <groupId>org.junit.jupiter</groupId>
  <artifactId>junit-jupiter-api</artifactId>
  <scope>compile</scope>
</dependency>
<dependency>
  <groupId>org.junit.jupiter</groupId>
  <artifactId>junit-jupiter-params</artifactId>
  <scope>compile</scope>
</dependency>
<dependency>
  <groupId>org.junit.jupiter</groupId>
  <artifactId>junit-jupiter-engine</artifactId>
  <scope>runtime</scope>
</dependency>

JUnit5优势

  • 支持lambda表达式,支持lambda表达式
  • JUnit5易扩展,包容性强,可以接入其他的测试引擎
  • 提供新的断言机制、参数化测试、重复性测试等新功能

JUnit5结构:

  • JUnit Platform:这是Junit提供的平台功能模块,通过它,其它的测试引擎都可以接入Junit实现接口和执行
  • JUnit JUpiter:这是JUnit5的核心,是一个基于JUnit Platform的引擎实现,它包含许多丰富的新特性来使得自动化测试更加方便和强大
  • JUnit Vintage:这个模块是兼容JUnit3、JUnit4版本的测试引擎,使得旧版本的自动化测试也可以在JUnit5下正常运行

实例

在JUnit 5中的一个测试类的生命周期基本是这样的:

class Lifecycle {
	@BeforeAll
	static void initializeExternalResources() {
	}
	@BeforeEach
	void initializeMockObjects() {
	}
	@Test
	void someTest() {
		assertTrue(true);
	}
	@Test
	void otherTest() {
		assumeTrue(true);
		assertNotEquals(1, 42, "Why wouldn't these be the same?");
	}
	@Test
	@Disabled
	void disabledTest() {
		System.exit(1);
	}
	@AfterEach
	void tearDown() {
	}
	@AfterAll
	static void freeExternalResources() {
	}
}

引入依赖,防止使用旧的junit4相关接口需将其排除

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
    <exclusions>
        <exclusion>
            <groupId>org.junit.vintage</groupId>
            <artifactId>junit-vintage-engine</artifactId>
        </exclusion>
    </exclusions>
</dependency>

注解

@BeforeEach:在每个单元测试方法执行前都执行一遍
@BeforeAll:在每个单元测试方法执行前执行一遍(只执行一次)
@DisplayName(“商品入库测试”):用于指定单元测试的名称
@Disabled:当前单元测试置为无效,即单元测试时跳过该测试
@RepeatedTest(n):重复性测试,即执行n次
@ParameterizedTest:参数化测试
@ValueSource(ints = {1, 2, 3}):参数化测试提供数据

假设、标签和禁止测试

假设、标签和禁止测试是JUnit 4的特性,在JUnit 5中仍然得以保留。不同的是假设中也支持Lambda表达式,假设的思想是如果假设条件没有得到满足,那么跳过测试执行。标签Tags等同于JUnit 4的测试分类的概念,可以对测试类和方法进行分类。JUnit 4禁止测试使用@Ignore注释,而在JUnit 5中则使用@Disabled注释。

Assert断言

JUnit 5的断言方法与JUnit 4相似,断言类提供assertTrue、assertEquals、assertNull、assertSame以及相反的断言方法。不同之处在于JUnit 5的断言方法支持Lambda表达式,并提供分组断言(Grouped Assertions)的新特性。分组断言允许执行一组断言,且会一起报告。在JUnit 4中,在一个测试中放入多个断言时,如果前面的断言执行失败(即验证失败),后面的断言将得不到执行。JUnit 5中使用分组断言就没有这个问题。
对JUnit 4的另一个改进是断言预期的异常。不再是以前那种把预期的异常类型放入@Test注释,或者是用try-catch包裹代码,JUnit 5使用assertThrows和equalsThrows断言。

JUnit 5支持Hamcrest匹配和AssertJ断言库,可以用它们来代替JUnit 5的方法。

JUnit Jupiter提供强大的断言方法用以验证结果,在使用时需要借助java8的新特性lambda表达式,均是来自org.junit.jupiter.api.Assertions包的static方法:

  • assertTrue与assertFalse用来判断条件是否为true或false
  • assertNull与assertNotNull用来判断条件是否为null
  • assertThrows用来判断执行抛出的异常是否符合预期,并可以使用异常类型接收返回值进行其他操作
  • assertTimeout用来判断执行过程是否超时
@Test
@DisplayName("测试断言超时")
void testTimeOut() {
    String actualResult = assertTimeout(ofSeconds(2), () -> {
        Thread.sleep(1000);
        return "a result";
    });
    System.out.println(actualResult);
}

assertAll是组合断言,当它内部所有断言正确执行完才算通过

@Test
@DisplayName("测试组合断言")
void testAll() {
    assertAll("测试item商品下单",
            () -> {
                // 模拟用户余额扣减
                assertTrue(1 < 2, "余额不足");
            },
            () -> {
                // 模拟item数据库扣减库存
                assertTrue(3 < 4);
            },
            () -> {
                // 模拟交易流水落库
                assertNotNull(new Object());
            }
    );
}

扩展模型

JUnit 5提供一套新的扩展API,取代以前的@RunWith和@Rule扩展机制。JUnit 4的测试类被限制到仅有一个Runner上,而新的扩展模型则允许一个类或方法keyii注册到多种扩展。

JUnit 5内建的扩展还支持方法级的依赖注入。

@ExtendWith(MockitoExtension.class)
@ExtendWith(CdiUnitExtension.class)
public class Test4 {
	@Test
	@DisplayName("awesome test")
	void dependencyInjection(TestInfo testInfo) {
		assertEquals("awesome test", testInfo.getDisplayName());
	}
}

Rule

JUnit Rule就是实现TestRule的类,作用类似于@Before、@After,用来在每个测试方法的执行前后执行一些代码的一个方法。

@Before、@After、@AfterClass、@BeforeClass都只能作用于一个类,如果同一个setup需要在两个类里面同时使用,那么你就要在两个测试类里面定义相同的@Before方法,然后里面写相同的代码,这就造成代码重复。
解决方法:

  1. 创建TestClass,然后继承;
  2. 使用JUnit Rule。另外,它还能做一些@Before这些Annotation做不到的事情。

怎么用JUnit Rule?

  1. 使用框架自带的Rule
    JUnit、Mockito自带JUnit Rule,如Timeout,TemporaryFolder。定义类的public field,然后添加注解@Rule。
public class TimeoutTest {
    @Rule
    public Timeout timeout = new Timeout(1000);
    @Test
    public void testMethod() throws Exception {
        //your tests
    }
}

对于TimeoutTest的每一个测试方法,运行时间不能超过1秒,不然会标志为失败。

  1. 自定义Rule
    需要实现TestRule接口重写apply()方法,返回一个Statement对象。例子:在测试方法运行之前,记录测试方法所在的类名和方法名,并打印出来:
public class TestRuleDemo implements TestRule {
    @Override
    public Statement apply(final Statement base, final Description description) {
        return new Statement() {
            @Override
            public void evaluate() throws Throwable {
                // 在base.evaluate()之前加上自定义的动作
                String className = description.getClassName();
                String methodName = description.getMethodName();
                base.evaluate();  // 运行测试方法
                // 在base.evaluate()之后加上自定义的动作
                System.out.println("Class name: " + className + ", method name: " + methodName);
            }
        };
    }
}

public class ExampleTest {
    @Rule
    public TestRuleDemo demo = new TestRuleDemo();
    @Test
    public void testAdd() throws Exception {
        assertEquals(11, 5 + 6);
    }
}

内嵌测试

JUnit5提供嵌套单元测试功能,可更好展示测试类之间的业务逻辑关系,通常是一个业务对应一个测试类,有业务关系的类其实可以写在一起。这样有利于进行测试。而且内联的写法可以大大减少不必要的类,精简项目,防止类爆炸等一系列问题。

@SpringBootTest
@AutoConfigureMockMvc
@DisplayName("Junit5单元测试")
public class MockTest {
    @Nested
    @DisplayName("内嵌订单测试")
    class OrderTestClas {
        @Test
        @DisplayName("取消订单")
        void cancelOrder() {
            int status = -1;
            System.out.println("取消订单成功,订单状态为:" + status);
        }
    }
}

JUnit Theories

使用JUnit Theories工具,测试被分成两个部分:一个是提供数据点集(比如待测试的数据)的方法,另一个是理论本身。这个理论看起来几乎就像一个测试,但是它有一个不同的注解(@Theory),并且它需要参数。类通过使用数据点集的任意一种可能的组合来执行所有理论。

使用assume使得你可以在对理论测试前首先检查一下前提条件。如果条件不是一个正确的给定参数集,那么此理论将会跳过此参数集。

除了简洁,由测试/理论模型实现的对测试数据进行的分离还有另外一点好处:你可能会开始考虑使你的测试数据独立于实际的东西来测试。

比如设计一个专门用来货币计算的计算器,首先需要给代码行为编写测试用例(这里以英镑 Pound 的乘法为例)
一个测试用例可能不够,需要再多一个:

@Test
public void multiplyPoundsByInteger () {
    assertEquals(10, new Pound(5).times(2).getAmount());
    assertEquals(15, new Pound(5).times(3).getAmount());
}

2个也不够,需要更多的测试用例。

// 利用变量来代替具体数据表达测试思想
public void multiplyAnyAmountByInteger(int amount, int multiplier) {
    assertEquals(amount * multiplier, new Pound(amount).times(multiplier).getAmount());
}

利用上面的方法,可将测试用例改写:

@Test
public void multiplyPoundsByInteger() {
    multiplyAnyAmountByInteger(5, 2);
    multiplyAnyAmountByInteger(5, 3);
}

以后若想增加测试用例,只要不停调用 multiplyAnyAmountByInteger 方法并赋予参数值即可。

方法 multiplyAnyAmountByInteger 就是理论的一个简单例子,理论就是一个带有参数的方法,其行为就是对任何参数都是正常的返回,不会抛出断言错误和其它异常。理论就是对一组数据进行概括性的陈述,如果没有对所有可能出现的情况都进行实验,是不能证明该理论是正确的,但是只要有一种错误情况出现,该理论就不成立。相反地,一个测试就是对一个单独数据的单独陈述,就像是一个科学理论的实验一样。

如何使用理论机制
在JUnit的理论机制中,每个测试方法不再是由注释 @Test 指定的无参测试函数,而是由注释 @Theory 指定的带参数的测试函数,这些参数来自一个数据集(data sets),数据集通过注释 @DataPoint 指定。

JUnit会自动将数据集中定义的数据类型和理论测试方法定义的参数类型进行比较,如果类型相同,会将数据集中的数据通过参数一一传入到测试方法中。数据集中的每一个数据都会被传入到每个相同类型的参数中。这时有人会问了,如果参数有多个,而且类型都和数据集中定义的数据相同,怎么办?答案是,JUnit会将这些数据集中的数据进行一一配对组合(所有的组合情况都会被考虑到),然后将这些数据组合统统通过参数,一一传入到理论的测试方法中,但是用户可以通过假设机制(assumption)在断言函数(assertion)执行这些参数之前,对这些通过参数传进来的数据集中的数据进行限制和过滤,达到有目的地部分地将自己想要的参数传给断言函数来测试。只有满足所有假设的数据才会执行接下来的测试用例,任何一个假设不满足的数据,都会自动跳过该理论测试函数(假设 assumption 不满足的数据会被忽略,不再执行接下来的断言测试),如果所有的假设都满足,测试用例断言函数不通过才代表着该理论测试不通过。

import static org.hamcrest.Matchers.*; //指定接下来要使用的Matcher匹配符  
import static org.junit.Assume.*; //指定需要使用假设assume*来辅助理论Theory  
import static org.junit.Assert.*; //指定需要使用断言assert*来判断测试是否通过  
  
import org.junit.experimental.theories.DataPoint;   //需要使用注释@DataPoint来指定数据集  
import org.junit.experimental.theories.Theories; //接下来@RunWith要指定Theories.class   
import org.junit.experimental.theories.Theory; //注释@Theory指定理论的测试函数  
import org.junit.runner.RunWith; //需要使用@RunWith指定接下来运行测试的类  
  
//注意:必须得使用@RunWith指定Theories.class  
@RunWith(Theories.class)  
public class TheoryTest {  
  
    //利用注释@DataPoint来指定一组数据集,这些数据集中的数据用来证明或反驳接下来定义的Theory理论,  
    //testNames1和testNames2这两个理论Theory测试函数的参数都是String,所以Junit4.4会将这5个  
    //@DataPoint定义的String进行两两组合,统统一一传入到testNames1和testNames2中,所以参数名year  
    //和name是不起任何作用的,"2007"同样有机会会传给参数name,"Works"也同样有机会传给参数year  
    @DataPoint public static String YEAR_2007 = "2007";  
    @DataPoint public static String YEAR_2008 = "2008";  
    @DataPoint public static String NAME1 = "developer";  
    @DataPoint public static String NAME2 = "Works";  
    @DataPoint public static String NAME3 = "developerWorks";  
  
    //注意:使用@Theory来指定测试函数,而不是@Test  
    @Theory   
    public void testNames1( String year, String name ) {  
        assumeThat( year, is("2007") ); //year必须是"2007",否则跳过该测试函数  
        System.out.println( year + "-" + name );  
        assertThat( year, is("2007") ); //这里的断言语句没有实际意义,这里举此例只是为了不中断测试  
    }  
  
    //注意:使用@Theory来指定测试函数,而不是@Test  
    @Theory  
    public void testNames2( String year, String name ) {  
        assumeThat(year, is("2007")); //year必须是"2007",否则跳过该测试函数  
        //name必须既不是"2007"也不是"2008",否则跳过该测试函数  
        assumeThat(name, allOf( not(is("2007")), not(is("2008"))));  
        System.out.println( year + "-" + name );  
        assertThat( year, is("2007") ); //这里的断言语句没有实际意义,这里举此例只是为了不中断测试  
    }
}

结果输出:
第一个Theory打印出:
2007-2007
2007-2008
2007-developer
2007-Works
2007-developerWorks
第二个Theory打印出:
2007-developer
2007-Works
2007-developerWorks

重复性测试

场景:对幂等性接口的测试。通过使用@RepeatedTest(n)指定需要重复的次数,可用于类和方法

参数化测试

在实际项目中,会遇到一些分支语句,一个测试用例已经不能覆盖全部分支语句。参数化测试主要包括五个步骤:

  1. 为准备使用参数化测试的测试类指定特殊的运行器org.junit.runners.Parameterized
  2. 为测试类声明几个变量,分别用于存放期望值和测试所用数据
  3. 为测试类声明一个带有参数的公共构造函数,并在其中为第二个环节中声明的几个变量赋值
  4. 为测试类声明一个使用注解org.junit.runners.Parameterized.Parameters修饰的,返回值为java.util.Collection的公共静态方法,并在此方法中初始化所有需要测试的参数对
  5. 编写测试方法,使用定义的变量作为参数进行测试

待测方法:

// 多分支语句
public boolean parameterization(int a){
	if ( a > 10 ) {
		return true;
	} else {
		return false;
	}
}
@RunWith(Parameterized.class) // 第一步
public class FirstDemoTestParameterization {
	// 要测试的类
    private FirstDemo firstDemo;
	// 第二步:为测试类声明几个变量,分别用于存放期望值和测试所用数据
	private int input1;
    private boolean expected;
	 
	@Before
	publicvoid setUp() throws Exception {
		firstDemo = new FirstDemo();
	}
 
    // 第三步:带有参数的公共构造函数,并在其中为声明的几个变量赋值
    public FirstDemoTestParameterization(int input1, boolean expected) {
       this.input1 = input1;  // 参数1
       this.expected = expected;  //期待的结果值
    }
	// 第四步:为测试类声明一个注解@Parameters,返回值为Collection的公共静态方法,并初始化所有需要测试的参数对
   @Parameters
   public static Collection prepareData() {
       Object[][] object = {{-1, true}, {13, true}}; // 测试数据
       return Arrays.asList(object); // 将数组转换成集合返回
    }
    
    @Test
    publicvoidtestParameterization() {
		// 第五步:编写测试方法,使用定义的变量作为参数进行测试。
		assertEquals(expected, firstDemo.Parameterization(input1));
    }
}

参数化测试:

  1. @RunWith(Parameterized.class):在每个需要参数化测试的类上面,都需要写上@RunWith(Parameterized.class)因为这样JUnit才会使用Parameterized运行器来运行测试,否则JUnit会选择默认的运行器,而默认运行器的不支持参数化测试
  2. @Parameters:在提供数据的static方法上加@Parameters,返回Collection

参数化测试可以按照多个参数分别运行多次单元测试这里有点类似于重复性测试,只不过每次运行传入的参数不用。需要使用到@ParameterizedTest,同时也需要@ValueSource提供一组数据,支持八种基本类型以及String和自定义对象类型。

@ParameterizedTest
@ValueSource(ints = {1, 2, 3})
@DisplayName("参数化测试")
void paramTest(int a) {
    assertTrue(a > 0 && a < 4);
}

测试数据外部化

JUnit4及TestNG支持外部化测试数据,以便可以针对不同的数据集运行测试用例,而无需更改源代码。

MathChecker类有方法可以检查一个数字是否是奇数:

public class MathChecker {
    public Boolean isOdd(int n){
        if (n % 2 != 0) {
            return true;
        } else {
            returnfalse;
        }
    }
}

以下是MathChecker类的TestNG测试用例:

public class MathCheckerTest {
    private MathChecker checker;
    @BeforeMethod
    public void beforeMethod() {
    	checker = new MathChecker();
    }
    @Test
    @Parameters("num")
    public void isOdd(int num) { 
		Boolean result = checker.isOdd(num);
		Assert.assertEquals(result, new Boolean(true));
    }
}

为测试用例提供数据的2种方式:

  1. 配置文件testng.xml
  1. DataProvider注解

JUnit与TestNG类似,测试数据也可以外部化用于JUnit。以下是与上述相同MathChecker类的JUnit测试用例:

@RunWith(Parameterized.class)
public class MathCheckerTest {
    private int inputNumber;
    private Boolean expected;
    private MathChecker mathChecker;
    @Before
    public void setup() {
        mathChecker = new MathChecker();
    }
    // Inject via constructor
    public MathCheckerTest(int inputNumber, Boolean expected){
        this.inputNumber = inputNumber;
        this.expected = expected;
    }
    
    @Parameterized.Parameters
    public static Collection<Object[]> getTestData() {
        return Arrays.asList(new Object[][]{
                {1, true},
                {2, false},
                {3, true},
                {4, false},
                {5, true}
        });
    }
    @Test
    public void testisOdd() {
        System.out.println("Running test for:" + inputNumber);
        assertEquals(mathChecker.isOdd(inputNumber), expected);
    }
}

要对其执行测试的测试数据由getTestData()方法指定。此方法可以轻松地修改为从外部文件读取数据,而不是硬编码数据。

隔离测试

防止多类之间依赖的测试。比如B层对D层的依赖。测试B层时,不可能还要跑D层,这样的话就不是单元测试。不需要跑D层,但是又需要D层的返回值。隔离测试解决这个问题。用Mockito来进行隔离测试。隔离测试,即Mock,模拟的功能。当依赖其他类时,不需要真实调用,只需模拟出该类即可。

Public boolean IsExist(Student student) { 
	List studentList = null;
	Boolean isExist = true; 
	if (student.getName() != null) {
		studentList = studentDao.queryStudent(student.getName());
		if (studentList.isEmpty()) {
			isExist = false;
		} else {
			isExist = true;
		}
	}
	return isExist;
}

测试方法:

import static org.junit.Assert.*;
import static org.mockito.Mockito.when;

@RunWith(MockitoJUnitRunner.class)
public class StudentServiceTest {
	@Mock
	StudentDao studentDao;
	
	@InjectMocks //自动注入Mock类(StudentDao)到被测试类(StudentService),作为一个属性
	StudentService studentService;
	
	@Test
	public void testIsExist(){       
		//(1)实例化一个实体类expectStudent,并为其赋值
	   Student expectStudent = new Student();
	   expectStudent.setName("001");
	   //(2)实例化一个List集合,并将赋值后的expectStudent实体类放入集合中
	   List<Student> mockStudentList = newArrayList<Student>();
	   mockStudentList.add(expectStudent);
	   //(3)当调用模拟类的方法时,返回List集合
		when(studentDao.queryStudent(expectStudent.getName())).thenReturn(mockStudentList);
		//------------------(二)实际值:测试Service层方法,并且返回实际值-----------
		Student actualStudent1 = new Student();
		actualStudent1.setName("001");
		boolean res = studentService.IsExist(actualStudent1);
		//------------------(三)期望值与实际值比较:测试Service层方法,并且返回实际值
		assertTrue(studentService.IsExist(actualStudent1));
		Student actualStudent2 = new Student();
		actualStudent2.setName("002");
		boolean res2 = studentService.IsExist(actualStudent2);
		assertTrue(studentService.IsExist(actualStudent2));
	}
}

参考

JUnit Rule是什么
Junit 5新特性全集
JUnit Theories介绍

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

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

相关文章

银保监机构保险许可证数据(2007-2022年)

保险是金融系统的重要组成部分,保险的经济补偿和资金融通功能对维持金融系统的稳定,维护整个社会的安定起着不可或缺的作用,因此,从现实意义上来讲,对于我国保险业系统性风险的研究是很有必要性的。 通过对保险业系统性风险的类型、传播机制的研究,可以有效防范并降低我国保险…

MATLB|基于粒子群优化算法的智能微电网调度(含风、光、微型燃气轮机、电网输入微网、储能)

&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️❤️&#x1f4a5;&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑…

linux基本使用及服务器环境搭建

文章目录0.linux基本命令1.配置jdk环境变量2.安装tomcat3. mysql安装4.部署jar项目基本命令5. 前端项目继承到后端项目中0.linux基本命令 1.pwd 我在那里 2.who am i 我是谁 3.clear 清屏 4.ctrlc 强制停止 5.ip addr 查看地址 6.ping 是否联通网站 7.systemctl start|s…

分布式系统的解决方案,学好这个就够了

分布式、负载、消息队列等一些解决方案&#xff0c;在互联网公司应用已经非常普遍了&#xff0c;也是每一个程序员要成为技术专家、架构师必学的知识。 所以&#xff0c;今天给大家推荐一个开源项目&#xff0c;有关互联网项目常见的解决方案&#xff0c;通通都打包一起了。 …

10个 解放双手的 IDEA 插件,少些冤枉代码

正经干活用的 分享一点自己工作中得心应手的IDEA插件&#xff0c;可不是在插件商店随随便便搜的&#xff0c;都经过实战检验&#xff0c;用过的都说好。可能有一些大家用过的就快速划过就行了。 1、GenerateAllSetter 实际的开发中&#xff0c;可能会经常为某个对象中多个属性…

半监督下的点云

搬了这么个东东来~~ 不过他的名字有点容易叫大家混淆,所以没写标题上... 在训练阶段&#xff0c;只需少量的二维方框标注作为指导&#xff0c;本文的网络就可以从激光雷达方框中产生精确的具有三维属性的图像级长方体标注。 论文&#xff1a;https://arxiv.org/pdf/2211.0930…

【Dubbo3高级特性】「实战开发」自定义扩展实现Dubbo服务对外暴露的主机地址实战开发指南

内容主旨 本篇文章主要介绍了如何进行自定义Dubbo服务对外暴露的主机地址的实战技术方案&#xff0c;其中我们需要针对于服务提供者侧的host主机暴漏的目的以及如何进行定制化处理 特性说明 在Dubbo中&#xff0c;Provider启动时主要做两个事情 启动服务提供者的server端实…

基于C++实现(控制台+界面)通讯录管理系统【100010012】

个人通讯录管理系统 问题描述&#xff1a; 主要内容&#xff1a; 个人通讯录是记录了同学&#xff08;包含一起上学的学校名称&#xff09;、同事&#xff08;包含共事的单位名称&#xff09;、朋友&#xff08;包含认识的地点&#xff09;、亲戚&#xff08;包含称呼&#…

Python小炼(2):文件操作

"一封信&#xff0c;写下太多如果" 如果有一定语言基础的&#xff0c;一定对文件操作十分得"熟悉"!当然&#xff0c;这种熟悉是 引起人恼怒的 也不为过。 python 也有自己的文件操作&#xff0c;那它跟C\C又有何不同呢&#xff1f; 一、文件的基本操作 (…

中国宗教活动场所数据库(数据+python代码)

通常研究&#xff0c;宗教活动场所与公司避税行为&#xff0c;社会整体信任水平以及民营企业创始资金来源等元素相关联。例如&#xff0c;企业注册地的宗教传统负向影响公司避税&#xff0c;企业注册地的宗教传统通过提高管理者的道德意识和强化管理者的风险规避倾向两条机制抑…

Swagger总结

目录 简介&#xff1a; openAPI Springfox&#xff1a; 简介 Springfox的使用 SwaggerUI的使用 Swagger配置 设置扫描的包 设置范围 Swagger常用注解&#xff1a; 控制类、方法生成接口信息 ApiParam ApiModel ApiModelProperty ApiIgnore ApiImplicitParam 部分图片来自百…

SpringBoot日志详解

⭐️前言⭐️ &#x1f349;博客主页&#xff1a; &#x1f341;【如风暖阳】&#x1f341; &#x1f349;精品Java专栏【JavaEE进阶】、【JavaEE初阶】、【MySQL】、【数据结构】 &#x1f349;欢迎点赞 &#x1f44d; 收藏 ⭐留言评论 &#x1f4dd;私信必回哟&#x1f601; …

rocketMq相关机制

rocketMq相关机制 topic读写队列 perm字段表示Topic的权限。有三个可选项。 2&#xff1a;禁写禁订阅&#xff0c;4&#xff1a;可订 阅&#xff0c;不能写&#xff0c;6&#xff1a;可写可订阅 这其中&#xff0c;写队列会真实的创建对应的存储文件&#xff0c;负责消息写入。…

小蓝本 第一本《因式分解技巧》第四章 拆项与添项 笔记(第四天)

小蓝本 第一本《因式分解技巧》第四章 拆项与添项 笔记&#xff08;第四天&#xff09;前言拆项与添项目的方法分组分解走平均分配分组分解走瞄准公式旧事重提第二章公式(9)好题习题4题目题解错题题号改错经验前言 芜湖&#xff0c;坚持做小蓝本的第四天&#xff0c;今天的知识…

基于LSTM、BP神经网络实现电力系统负荷预测(Python代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

房屋装修设计技巧有哪些?有哪些注意事项

拥有自己的家是每个人的愿望&#xff0c;拥有一座新的房子是一种幸福。但是&#xff0c;作为一个装修小白&#xff0c;装修新房是一件很麻烦的事情。那么&#xff0c;房屋装修设计技巧是什么&#xff1f;房屋的装修设计应该注意些什么&#xff1f;下面我将详细解释一下。 房屋装…

pandas数据分析

目录 题目001&#xff1a; 把list变成一个Series 题目002&#xff1a; 把dict变成一个Series 题目003&#xff1a; 把Series转换成list 题目004&#xff1a; 把series变成一个DataFrame 题目005&#xff1a;用numpy创建Series 题目006&#xff1a;转换series的数据类型 …

【亲测可用】2022最新酒桌小游戏喝酒小程序源码_带流量主

内容目录一、详细介绍二、效果展示1.部分代码2.效果图展示三、学习资料下载一、详细介绍 喝酒神器3.6&#xff0c;原版本没有广告位&#xff0c;修改增加了广告位&#xff0c; 由多个喝酒小游戏组合而成,具体如下: 大话骰(带音效) 愤怒大叔(带音效,多个皮肤模板用户可选择) …

【大数据入门核心技术-ElasticSearch】(二)ElasticSearch整体架构和重要工作原理

目录 一、整体架构图 二、重要工作原理 1、文档写入原理 2、文档检索原理 一、整体架构 二、重要工作原理 1、文档写入原理 1&#xff09;选择任意一个DataNode发送请求&#xff0c;例如&#xff1a;node2。此时&#xff0c;node2就成为一个coordinating node&#xff08;…

我也和 chatGPT 聊了聊

我也和 chatGPT 聊了聊&#xff0c;都是因为最近 chatGPT 太火了&#xff01; 这是一个大型的 AI 语言模型。你不仅可以和它聊天&#xff0c;问它各种各样的问题&#xff0c;还可以让它写代码、写论文、解数学题、解bug&#xff0c;等等。 可以说&#xff0c;chatGPT 是目前最…