JAVA单元测试与集成测试详解

您所在的位置:网站首页 测试案例怎么写更全面一些 JAVA单元测试与集成测试详解

JAVA单元测试与集成测试详解

2024-07-09 17:22| 来源: 网络整理| 查看: 265

Java 测试要求

测试要求来源于阿里嵩山版Java开发手册:

【强制】好的单元测试必须遵守 AIR 原则。 说明:单元测试在线上运行时,感觉像空气(AIR)一样感觉不到,但在测试质量的保障上,却是非常关键的。好的单元测试宏观上来说,具有自动化、独立性、可重复执行的特点。 ⚫ A:Automatic(自动化) ⚫ I:Independent(独立性) ⚫ R:Repeatable(可重复)【强制】单元测试应该是全自动执行的,并且非交互式的。测试用例通常是被定期执行的,执 行过程必须完全自动化才有意义。输出结果需要人工检查的测试 不是一个好的单元测试。单元测试中不准使用 System.out 来进行人肉验证,必须使用 assert 来验证。【强制】保持单元测试的独立性。为了保证单元测试稳定可靠且便于维护,单元测试用例之间决不能互相调用,也不能依赖执行的先后次序。 反例:method2 需要依赖 method1 的执行,将执行结果作为 method2 的输入。【强制】单元测试是可以重复执行的,不能受到外界环境的影响。 说明:单元测试通常会被放到持续集成中,每次有代码 check in 时单元测试都会被执行。如果单测对外部 环境(网络、服务、中间件等)有依赖,容易导致持续集成机制的不可用。 正例:为了不受外界环境影响,要求设计代码时就把 SUT 的依赖改成注入,在测试时用 spring 这样的 DI框架注入一个本地(内存)实现或者 Mock 实现。【强制】对于单元测试,要保证测试粒度足够小,有助于精确定位问题。单测粒度至多是类级 别,一般是方法级别。 说明:只有测试粒度小才能在出错时尽快定位到出错位置。单测不负责检查跨类或者跨系统的交互逻辑, 那是集成测试的领域。【强制】核心业务、核心应用、核心模块的增量代码确保单元测试通过。 说明:新增代码及时补充单元测试,如果新增代码影响了原有单元测试,请及时修正。【强制】单元测试代码必须写在如下工程目录:src/test/java,不允许写在业务代码目录下。 说明:源码编译时会跳过此目录,而单元测试框架默认是扫描此目录。【推荐】单元测试的基本目标:语句覆盖率达到 70%;核心模块的语句覆盖率和分支覆盖率都 要达到 100% 说明:在工程规约的应用分层中提到的 DAO 层,Manager 层,可重用度高的 Service,都应该进行单元测试。【推荐】编写单元测试代码遵守 BCDE 原则,以保证被测试模块的交付质量。 ⚫ B:Border,边界值测试,包括循环边界、特殊取值、特殊时间点、数据顺序等。 ⚫ C:Correct,正确的输入,并得到预期的结果。 ⚫ D:Design,与设计文档相结合,来编写单元测试。 ⚫ E:Error,强制错误信息输入(如:非法数据、异常流程、业务允许外等),并得到预期的结果。 10.【推荐】对于数据库相关的查询,更新,删除等操作,不能假设数据库里的数据是存在的,或 者直接操作数据库把数据插入进去,请使用程序插入或者导入数据的方式来准备数据。 反例:删除某一行数据的单元测试,在数据库中,先直接手动增加一行作为删除目标,但是这一行新增数 据并不符合业务插入规则,导致测试结果异常。 11.【推荐】和数据库相关的单元测试,可以设定自动回滚机制,不给数据库造成脏数据。或者对单元测试产生的数据有明确的前后缀标识。 正例:在阿里巴巴企业智能事业部的内部单元测试中,使用 _ENTERPRISE_INTELLIGENCE UNIT_TEST _的前缀来标识单元测试相关代码。 12.【推荐】对于不可测的代码在适当的时机做必要的重构,使代码变得可测,避免为了达到测试 要求而书写不规范测试代码。 13.【推荐】在设计评审阶段,开发人员需要和测试人员一起确定单元测试范围,单元测试最好覆盖所有测试用例(UC)。 14.【推荐】单元测试作为一种质量保障手段,在项目提测前完成单元测试,不建议项目发布后补 充单元测试用例。 15.【参考】为了更方便地进行单元测试,业务代码应避免以下情况: ⚫ 构造方法中做的事情过多。 ⚫ 存在过多的全局变量和静态方法。 ⚫ 存在过多的外部依赖。 ⚫ 存在过多的条件语句。 说明:多层条件语句建议使用卫语句、策略模式、状态模式等方式重构。 16.【参考】不要对单元测试存在如下误解: ⚫ 那是测试同学干的事情。本文是开发手册,凡是本文内容都是与开发同学强相关的。 ⚫ 单元测试代码是多余的。系统的整体功能与各单元部件的测试正常与否是强相关的。 ⚫ 单元测试代码不需要维护。一年半载后,那么单元测试几乎处于废弃状态。 ⚫ 单元测试与线上故障没有辩证关系。好的单元测试能够最大限度地规避线上故障。 命名规格 类命名:Test,以它要测试的类的名称开始,以 Test 结尾。例如,OrderInfoTest方法命名:test,其中****是要测试的方法的名称。例如,testCalculateTotal()路径:使用test作为顶级包名 单元测试

定义:是指对软件中的最小可测试单元进行检查和验证。 Java里单元指一个方法。单元测试是在软件开发过程中要进行的最低级别的测试活动,软件的独立单元将在与程序的其他部分相隔离的情况下进行测试。

断言

断言(Assertion)是测试中常用的一种技术,用于验证测试的预期结果是否与实际结果相符。断言通常用于测试框架中,用于检查代码的输出、行为或状态是否符合预期。在Java中,常见的断言工具是JUnit框架提供的断言方法,例如assertEquals()、assertTrue()、**assertFalse()**等。这些方法接受一个预期值和一个实际值,如果两者相符,则测试通过;否则,测试失败并抛出异常。

//1、assertEquals(expected, actual):验证预期值和实际值是否相等。 assertEquals(10, result); // 预期值为10,实际值为result //2、assertTrue(condition):验证条件是否为真。 assertTrue(result > 0); // 验证result大于0 //3、assertFalse(condition):验证条件是否为假 assertFalse(result.isEmpty()); // 验证result不为空 //4、assertNull(object):验证对象是否为null assertNull(result); // 验证result为null //5、assertNotNull(object):验证对象是否不为null。 assertNotNull(result); // 验证result不为null

在Java中,也可以自定义断言来满足特定的测试需求。自定义断言可以根据你的应用程序逻辑进行定制化,以便更好地验证测试的预期结果。

//1、创建一个包含静态方法的类,用于执行自定义断言逻辑。 public class CustomAssertions { public static void assertEvenNumber(int number) { if (number % 2 != 0) { throw new AssertionError("Expected an even number, but got: " + number); } } } //2、在测试代码中使用自定义断言方法。 @Test public void testCustomAssertion() { int result = 10; CustomAssertions.assertEvenNumber(result); } //上述代码中自定义断言方法assertEvenNumber()用于验证给定的数字是否为偶数。如果数字不是偶数,断言会抛出AssertionError异常,测试将失败 SpringBootTest框架

Spring Boot Test框架是基于JUnit的测试框架,是用于编写和执行单元测试、集成测试和端到端测试的测试框架,专门针对Spring Boot应用程序进行测试。框架提供了一系列注解、类和工具,以简化测试的编写和执行过程,并提供了与Spring应用程序的集成和自动化配置 以下是Spring Boot Test框架的一些关键特点和功能:

自动配置:Spring Boot Test框架基于Spring Boot的自动配置原理,可以自动配置测试环境,包括数据库、缓存、消息队列等。注解驱动:Spring Boot Test框架使用注解来标记和配置测试类和方法,例如**@SpringBootTest**、@RunWith、@DataJpaTest等。依赖注入:Spring Boot Test框架支持依赖注入,可以使用**@Autowired**注解注入需要测试的对象或模拟对象。模拟对象:Spring Boot Test框架与Mockito和EasyMock等模拟对象框架集成,方便创建和操作模拟对象。集成测试:Spring Boot Test框架提供了对Web应用程序的集成测试支持,可以模拟HTTP请求和响应,并进行端到端的测试。数据库测试:Spring Boot Test框架支持数据库相关测试,包括内存数据库的自动配置和管理,以及数据访问层的单元测试和集成测试。测试工具:Spring Boot Test框架提供了一些测试工具类和辅助方法,例如TestRestTemplate用于发送HTTP请求,TestEntityManager用于操作JPA实体管理器等。 所需依赖 // 基础依赖 spring-boot-starter-test中包含了junit和mockito等依赖 // pom.xml org.springframework.boot spring-boot-starter-test test // gradle testCompile 'org.springframework.boot:spring-boot-starter-test' //还可以根据需要添加其他测试相关的依赖项,并确保它们与Spring Boot版本兼容 常用注解

@SpringBootTest注解:该注解用于标记测试类,指示Spring Boot在测试环境中加载完整的应用程序上下文,注解用于在Spring Boot的测试环境中加载完整的应用程序上下文。它会根据配置文件和注解自动配置应用程序的各个组件,并创建一个与实际运行环境相似的测试环境。通过加载完整的应用程序上下文,可以进行更全面的集成测试,包括对依赖关系的正确性和整个应用程序的功能进行验证 @RunWith:该注解用于指定测试运行器,用于执行测试。在Spring Boot中通常使用**@RunWith(SpringRunner.class)**。

1. **BlockJUnit4ClassRunner**:默认的运行器,用于运行基于 JUnit 4 的测试类。 2. **PowerMockRunner**:用于支持使用 PowerMock 框架进行单元测试,可以模拟静态方法、私有方法等 3. **AndroidJUnit4**:用于在 Android 环境中运行测试,支持 Android 特定的功能和断言 4. **SpringRunner** 是 JUnit 4 的一个运行器(Runner),用于在 Spring 环境中运行测试,继承自 JUnit 的 **BlockJUnit4ClassRunner**,它提供了与 Spring 框架集成的功能。当使用 **SpringRunner** 运行测试类时,会自动创建和管理 Spring 容器,并将依赖注入到测试类中,使得可以在测试中使用 Spring 的功能,如依赖注入、事务管理、AOP 等

@MockBean: 该注解用于创建一个模拟对象(Mock)并将其注入到应用程序上下文中。它通常用于模拟依赖的外部组件或服务 @Autowired: 该注解用于自动注入依赖。它可以用于将被测试对象或其他组件注入到测试类中。 @Test:该注解用于标记测试方法。测试方法应该使用该注解进行注释,以便在执行测试时被识别。@Test(timeout = 1000) 测试方法执行超过1000毫秒后算超时,测试将失败。@Test(expected = Exception.class) 测试方法期望得到的异常类,如果方法执行没有抛出指定的异常,则测试失败。 @Before: 该注解用于标记在每个测试方法之前执行的方法。它通常用于准备测试环境,例如初始化对象或设置测试数据 @After: 该注解用于标记在每个测试方法之后执行的方法。它通常用于清理测试环境,例如释放资源或重置状态

测试案例 @RunWith(SpringRunner.class) // 使用Spring环境测试 @SpringBootTest //指示Spring Boot在测试环境中加载完整的应用程序上下文(整个应用程序的配置和运行环境) public class Test { @Autowired private MyService myService; @Test public void testAddNumbers() { int result = myService.addNumbers(2, 3); assertEquals(5, result); } } import net.remote.response.AccountApiResponse; import net.service.UserService; import net.site.SiteInfoService; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest @EnableAutoConfiguration public class SpBootMokDemo{ @Autowired private SiteInfoService siteInfoService; @MockBean private UserService userService; @Before public void setup() throws Exception { // 模拟方法调用的返回值 AccountApiResponse.AccountDetailReturn accountDetailReturn = new AccountApiResponse.AccountDetailReturn(); accountDetailReturn.setStatus("okk"); Mockito.when(userService.getUserInfoById("aaa")).thenReturn(accountDetailReturn); //模拟方法 参照PowerMock测试框架 } @Test public void testCase()throws Exception { System.out.println(userService.getUserInfoById("aaa")); } } Mocking

Mocking是软件开发中的一种技术,用于模拟或替代系统中的依赖项,以便进行单元测试。通过模拟依赖项的行为,我们可以控制测试环境并隔离被测代码的影响。在进行单元测试时,我们可以使用mocking框架创建和配置模拟对象,以替代实际的依赖项。通过定义模拟对象的行为和返回值,我们可以模拟依赖项的响应,以便在测试中验证被测代码的行为。 Mocking通常用于以下情况:

当某个依赖项不容易创建或访问,例如数据库连接、网络请求或外部服务。当某个依赖项的行为不稳定或不可靠,例如随机生成的数据或时间相关的操作。当某个依赖项的使用会导致副作用,例如修改数据库或发送电子邮件。

常见的mocking框架包括:

Mockito:用于Java的常见mocking框架,易于使用且功能强大。PowerMock:在Mockito基础上扩展,支持更多的mocking场景,如静态方法、私有方法和构造函数等。EasyMock:另一个流行的Java mocking框架,提供了简单的API和易于理解的语法。Jest:用于JavaScript的mocking框架,适用于前端开发和Node.js环境。Sinon.js:JavaScript的另一个流行mocking框架,可以与Jest等测试框架集成使用。

使用这些mocking框架,可以轻松地创建模拟对象,并使用断言和验证来验证被测代码与依赖项之间的交互。这样就可以更好地控制测试环境,减少外部因素对测试结果的影响,并提高测试的可靠性和可重复性

Mockito框架

Mockito是一个用于Java的流行的mocking框架,用于进行单元测试和行为驱动开发(BDD)。它提供了简洁而强大的API,使开发人员可以轻松地创建和配置模拟对象,并验证被测代码与依赖项之间的交互。

所需依赖 // 1、添加Spring Boot Starter Test依赖,含了JUnit和Mockito的依赖会自动引入JUnit和Mockito无需单独添加它们的依赖 //在测试类中使用@RunWith(SpringRunner.class)注解,可与Spring Boot集成测试一起使用Mockito。可以使用Mockito来模拟Bean与注入依赖 org.springframework.boot spring-boot-starter-test test //2、引入Mockito的核心库 //这只是Mockito的核心依赖,如果需要使用Mockito的其他功能或扩展,可能需要添加其他相关依赖 org.mockito mockito-core 3.11.2 test //除了Mockito本身的依赖,还需要确保项目中包含JUnit或其他适当的测试框架的依赖,因为Mockito通常与测试框架一起使用 //添加JUnit的依赖 , 引入整个 JUnit Jupiter 模块 org.junit.jupiter junit-jupiter 5.8.1 test 常用方法 Mockito.mock(classToMock) //模拟对象 可以使用@Mock注解代替 Mockito.verify(mock) //验证行为是否发生 Mockito.when(methodCall).thenReturn(value1).thenReturn(value2) //触发时第一次返回value1,第n次都返回value2 Mockito.doThrow(toBeThrown).when(mock).[method] //模拟抛出异常。 Mockito.mock(classToMock,defaultAnswer) //使用默认Answer模拟对象 Mockito.when(methodCall).thenReturn(value) //参数匹配 Mockito.doReturn(toBeReturned).when(mock).[method] //参数匹配(直接执行不判断) Mockito.when(methodCall).thenAnswer(answer)) //预期回调接口生成期望值 Mockito.doAnswer(answer).when(methodCall).[method] //预期回调接口生成期望值(直接执行不判断) Mockito.spy(Object) //用spy监控真实对象,设置真实对象行为 Mockito.doNothing().when(mock).[method] //不做任何返回 Mockito.doCallRealMethod().when(mock).[method] //等价于Mockito.when(mock.[method]).thenCallRealMethod(); 调用真实的方法 reset(mock) //重置mock //Mockito继承Matchers,anyInt()等均为Matchers方法,使用了参数匹配,那么所有的参数都必须通过matchers来匹配,当传入两个参数 Mockito.anyInt() //任何 int 值 ; Mockito.anyLong() //任何 long 值 ; Mockito.anyString() //任何 String 值 ; Mockito.anyBoolean() //任何boolean 值 Mockito.any(XXX.class) //任何 XXX 类型的值 等等 Mockito.any() //任何参数 使用案例 //验证行为是否发生 List mock = Mockito.mock(List.class);//模拟创建一个List对象 mock.add(1);//调用mock对象的方法 mock.clear(); Mockito.verify(mock).add(1);//验证方法是否执行 //多次触发返回不同值 Iterator iterator = mock(Iterator.class);//mock一个Iterator类 Mockito.when(iterator.next()).thenReturn("hello").thenReturn("world");//预设当iterator调用next()时第一次返回hello,第n次都返回world String result = iterator.next() + " " + iterator.next() + " " + iterator.next();//使用mock的对象 Assert.assertEquals("hello world world",result);//验证结果 //模拟抛出异常 @Test(expected = IOException.class)//期望报IO异常 public void when_thenThrow() throws IOException{ OutputStream mock = Mockito.mock(OutputStream.class); Mockito.doThrow(new IOException()).when(mock).close();//预设当流关闭时抛出异常 mock.close(); } //参数匹配 @Test public void with_arguments(){ TestB b = Mockito.mock(TestB.class); Mockito.when(b.getSex(1)).thenReturn("男");//预设根据不同的参数返回不同的结果 Mockito.when(b.getSex(2)).thenReturn("女"); Assert.assertEquals("男", b.getSex(1)); Assert.assertEquals("女", b.getSex(2)); Assert.assertEquals(null, b.getSex(0));//对于没有预设的情况会返回默认值 } @Data class TestB{ private String name; public String getSex(Integer sex){ if(sex==1){ return "man"; }else{ return "woman"; } } } // 匹配参数 @Before public void setup() throws Exception{ // 模拟鉴权方法调用的返回值 : 输入任意参数 返回 true Mockito.when(accountService.isMain(Mockito.any())).thenReturn(true); // 模拟云盘剩余额度方法请求的返回值 : 输入任意参数 返回 10086 Mockito.when(rulesService.getAllowSSDVolumeSize(Mockito.any(),Mockito.any())).thenReturn(10086); // 模拟实例剩余额度方法请求的返回值 : 输入任意参数 返回 10086 Mockito.when(rulesService.getAllowCreateVmNum(Mockito.anyBoolean(),Mockito.any(),Mockito.any())).thenReturn(10086); } //重置 mock @Test public void reset_mock(){ List list = mock(List.class); Mockito. when(list.size()).thenReturn(10); list.add(1); Assert.assertEquals(10,list.size()); //重置mock,清除所有的互动和预设 Mockito.reset(list); Assert.assertEquals(0,list.size()); } @Mock注解

@Mock注解是Mockito框架提供的一个注解,用于创建一个模拟对象(Mock Object)。可以理解为对 mock 方法的一个替代。使用该注解时,要使用MockitoAnnotations.initMocks 方法,让注解生效。旧版的是initMocks,新版的是openMocks(从Mockito版本3.4.0开始引入的新方法)。也可以用MockitoJUnitRunner来代替MockitoAnnotations.initMocks

import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.util.Random; import static org.mockito.Mockito.*; public class MockitoDemo { @Mock private Random random; @Before public void before() { // 让注解生效 MockitoAnnotations.initMocks(this); } @Test public void test() { when(random.nextInt()).thenReturn(100); Assert.assertEquals(100, random.nextInt()); } } // 也可以用MockitoJUnitRunner来代替MockitoAnnotations.initMocks import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import java.util.Random; import static org.mockito.Mockito.*; @RunWith(MockitoJUnitRunner.class) public class MockitoDemo { @Mock private Random random; @Test public void test() { when(random.nextInt()).thenReturn(100); Assert.assertEquals(100, random.nextInt()); } }

使用**@Mock注解和Mockito.mock(classToMock)方法都可以创建模拟对象,它们的功能是相同的。它们都可以用于模拟依赖对象,以便进行单元测试时进行行为验证和结果验证。@MockBean注解是Spring Boot框架提供的注解,它是基于Mockito的@Mock注解的增强版。@MockBean注解除了创建模拟对象外,还会将模拟对象注入到Spring应用程序上下文中,替换实际的bean。这样,在进行集成测试时,可以模拟和控制应用程序中的依赖对象。 @Mock注解和Mockito.mock(classToMock)**之间存在的差异:

语法:@Mock注解是一种基于注解的方式,可以直接在测试类中使用。而**Mockito.mock(classToMock)**是一种方法调用,需要在测试方法中显式地调用。初始化:使用**@Mock注解时,通常需要配合MockitoAnnotations.initMocks(this)或MockitoJUnitRunner来初始化模拟对象。而使用Mockito.mock(classToMock)**方法时,模拟对象会立即被创建。可读性:@Mock注解的使用可以更清晰地表达测试类中哪些对象是模拟对象,使代码更易读。而使用**Mockito.mock(classToMock)**方法则需要显式地指定模拟对象的创建。

@Mock注解和Mockito.mock(classToMock)与@MockBean之间存在的差异:

使用范围:@Mock注解与Mockito.mock(classToMock)主要用于单元测试中,而@MockBean注解主要用于集成测试中。Spring上下文:@MockBean注解会将模拟对象注入到Spring应用程序上下文中,以便在整个应用程序中使用。而**@Mock注解与Mockito.mock(classToMock)**不会自动将模拟对象注入到Spring上下文中。自动配置:@MockBean注解还可以与**@SpringBootTest**或其他Spring Boot测试注解一起使用,以便自动配置模拟对象。它会自动创建模拟对象,并将其注册到应用程序上下文中,以供其他组件使用。

总体而言,无论是使用**@Mock注解还是Mockito.mock(classToMock)方法,它们都是用于创建模拟对象的工具。选择使用哪种方式取决于个人喜好和项目的约定。@Mock注解与Mockito.mock(classToMock)用于基本的单元测试中创建模拟对象,而@MockBean**注解是Spring Boot提供的用于集成测试的注解,除了创建模拟对象外,还会自动将其注入到Spring上下文中。选择使用哪个注解取决于测试场景和需求。

PowerMock测试框架

PowerMock 是一个 Java 测试框架,它扩展了 Mockito 和 EasyMock,提供了更强大的能力,可以模拟和测试一些传统的难以测试的场景,如静态方法、私有方法、构造函数等。使用 PowerMock 可以帮助开发人员解决一些传统的难以测试的问题,提高代码的测试覆盖率和质量。在使用 PowerMock 进行测试时,通常需要结合 JUnit、Mockito 或 EasyMock 等测试框架来完成测试代码的编写。 PowerMock 的主要特点和功能包括:

模拟静态方法:PowerMock 可以模拟静态方法的行为,使得测试静态方法的代码变得简单和可控。模拟私有方法:PowerMock 允许模拟私有方法,以便进行私有方法的单元测试。模拟构造函数:PowerMock 可以模拟对象的构造函数,包括私有构造函数和带参数的构造函数。模拟 final 类和方法:PowerMock 可以模拟 final 类和方法,以便进行对它们的测试。模拟静态初始化块:PowerMock 允许模拟静态初始化块的行为。 所需依赖 // 引入依赖 testImplementation 'org.powermock:powermock-api-mockito2:2.0.9' testImplementation 'org.powermock:powermock-module-junit4:2.0.9' 模拟静态方法 // 假设有一个包含静态方法的类 public class MyUtils { public static String generateUniqueId() { // 生成唯一标识符的逻辑 return UUID.randomUUID().toString(); } } // 使用 PowerMock 模拟静态方法 @RunWith(PowerMockRunner.class) @PrepareForTest(MyUtils.class) public class MyTestClass { @Test public void testGenerateUniqueId() { PowerMockito.mockStatic(MyUtils.class); Mockito.when(MyUtils.generateUniqueId()).thenReturn("mockedId"); String result = MyUtils.generateUniqueId(); assertEquals("mockedId", result); } } 模拟私有方法 // 假设有一个包含私有方法的类 public class MyClass { private String getPrivateData() { // 私有方法的逻辑 return "private data"; } public String processData() { String privateData = getPrivateData(); // 处理私有数据的逻辑 return "processed data: " + privateData; } } // 使用 PowerMock 模拟私有方法 @RunWith(PowerMockRunner.class) @PrepareForTest(MyClass.class) public class MyTestClass { @Test public void testProcessData() throws Exception { MyClass mockedInstance = PowerMockito.spy(new MyClass()); PowerMockito.when(mockedInstance, "getPrivateData").thenReturn("mocked private data"); String result = mockedInstance.processData(); assertEquals("processed data: mocked private data", result); } } 模拟构造方法 // 假设有一个需要模拟构造函数的类 public class MyObject { public MyObject() { // 构造函数的逻辑 } public String processData() { // 处理数据的逻辑 return "processed data"; } } // 使用 PowerMock 模拟构造函数 @RunWith(PowerMockRunner.class) @PrepareForTest(MyObject.class) public class MyTestClass { @Test public void testProcessData() throws Exception { MyObject mockedInstance = PowerMockito.mock(MyObject.class); PowerMockito.whenNew(MyObject.class).withNoArguments().thenReturn(mockedInstance); String result = mockedInstance.processData(); assertEquals("processed data", result); } } 模拟 final 类和方法 //############### 模拟 final 类 // 假设有一个 final 类 public final class FinalClass { public String getData() { return "data"; } } // 使用 PowerMock 模拟 final 类 @RunWith(PowerMockRunner.class) @PrepareForTest(FinalClass.class) //使用 @PrepareForTest 注解来指定要模拟的类 public class MyTestClass { @Test public void testFinalClass() { FinalClass mockedInstance = PowerMockito.mock(FinalClass.class); Mockito.when(mockedInstance.getData()).thenReturn("mocked data"); String result = mockedInstance.getData(); assertEquals("mocked data", result); } } //############### 模拟 final 方法 // 假设有一个包含 final 方法的类 public class MyClass { public final String getData() { return "data"; } } // 使用 PowerMock 模拟 final 方法 @RunWith(PowerMockRunner.class) @PrepareForTest(FinalClass.class) //使用 @PrepareForTest 注解来指定要模拟的类 public class MyTestClass { @Test public void testFinalMethod() throws Exception { MyClass mockedInstance = PowerMockito.mock(MyClass.class); Mockito.when(mockedInstance.getData()).thenReturn("mocked data"); String result = mockedInstance.getData(); assertEquals("mocked data", result); } } 模拟静态初始化块 // 假设有一个包含静态初始化块的类 public class MyClass { static { // 静态初始化块的逻辑 } public String getData() { return "data"; } } // 使用 PowerMock 模拟静态初始化块 调用静态块时不做操作 @RunWith(PowerMockRunner.class) @PrepareForTest(MyClass.class)//使用 @PrepareForTest 注解来指定要模拟的类 public class MyTestClass { @Test public void testStaticInitializer() throws Exception { PowerMockito.mockStatic(MyClass.class); // PowerMock 提供的静态方法,用于模拟静态方法和静态初始化块 PowerMockito.doNothing().when(MyClass.class, "clinit");// PowerMock 的方法,用于指定当调用指定类的静态初始化块时执行的操作,doNothing() 方法表示在调用静态初始化块时不执行任何操作 MyClass mockedInstance = new MyClass(); String result = mockedInstance.getData(); assertEquals("data", result); } } // 使用 PowerMock 模拟静态初始化块 调用静态块时做操作 @RunWith(PowerMockRunner.class) @PrepareForTest(MyClass.class) public class MyTestClass { @Test public void testStaticInitializer() throws Exception { PowerMockito.mockStatic(MyClass.class); PowerMockito.doAnswer(invocation -> { //使用 PowerMock 的 doAnswer() 方法结合 Mockito 的 Answer 接口来模拟静态块操作 // 在静态初始化块调用时执行的操作 System.out.println("Performing custom operation during static initialization"); // 返回自定义的结果 return null; }).when(MyClass.class, "clinit"); MyClass mockedInstance = new MyClass(); String result = mockedInstance.getData(); assertEquals("data", result); } } 集成测试

集成测试是它是一种测试方法,用于验证多个组件、模块或系统之间的协同工作。它主要关注不同部分之间的交互和集成,以确保整个系统按预期工作。 集成测试的目的是验证不同组件之间的接口、数据传递和协作是否正常。它可以帮助发现系统集成方面的问题,确保系统的整体功能和性能达到预期。集成测试通常在单元测试之后进行,以确保单元测试通过后的组件在集成时能够正常工作。 一般的集成测试流程:

确定测试范围:确定要进行集成测试的组件、模块或系统。准备测试环境:设置测试环境,包括所需的硬件、软件、数据库、网络等。定义测试用例:根据需求和设计规格,编写测试用例,覆盖不同的集成场景和交互情况。准备测试数据:根据测试用例,准备必要的测试数据,以模拟实际环境中的数据交换和处理。执行测试:执行集成测试,按照定义的测试用例对不同组件之间的交互进行测试。检查和记录结果:验证每个测试用例的执行结果,记录通过和失败的测试用例,并检查是否符合预期结果。分析和修复问题:如果测试中发现问题或错误,分析其原因,并修复或调整相应的组件或系统。进行回归测试:在修复问题后,进行回归测试以确保修复不会引入新的问题。清理和整理:在集成测试完成后,清理测试环境和数据,并进行必要的文档整理和总结。 import net.remote.response.AccountApiResponse; import net.service.UserService; import net.site.SiteInfoService; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest @EnableAutoConfiguration public class SpBootMokDemo{ @Autowired private SiteInfoService siteInfoService; @MockBean private UserService userService; @Before public void setup() throws Exception { // 模拟方法调用的返回值 AccountApiResponse.AccountDetailReturn accountDetailReturn = new AccountApiResponse.AccountDetailReturn(); accountDetailReturn.setStatus("okk"); Mockito.when(userService.getUserInfoById("aaa")).thenReturn(accountDetailReturn); } @Test public void testCase()throws Exception { System.out.println(userService.getUserInfoById("aaa")); } }

在springboot集成测试中需要使用@RunWith(SpringRunner.class)创建和使用spring容器,加载应用上下文。所以在测试时如果有静态方法的外部依赖就无法使用PowerMock做到有效模拟(由于PowerMock需要使用到@RunWith(PowerMockRunner.class)用于支持使用 PowerMock 框架进行单元测试,可以模拟静态方法、私有方法等)。(经过调研暂未找到可以同时使用SpringBootTest框架进行针对springboot应用程序进行测试的同时模拟静态方法依赖的技术手段,需后续调研) 推荐在开发业务功能时,避免使用静态方法调用外部环境。可以代替为普通public方法,并将该类交由spring容器管理。 交由spring容器管理的类都可以使用@MockBean:(该注解用于创建一个模拟对象(Mock)并将其注入到应用程序上下文中。它通常用于模拟依赖的外部组件或服务)进行模拟



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3