0


SpringBoot项目中单元测试与集成测试的应用

测试分类

单元测试->集成测试->系统测试->验收测试

  1. 单元测试单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证。一个单元可能是单个程序、类、对象、方法等。测试阶段:编码后或编码前(测试驱动开发TDD)测试对象:最小模块(Java-类)测试内容:模块接口测试、局部数据结构、路径测试、错误处理测试、边界测试模块接口测试:对通过被测模块的数据流进行测试。包括参数表、调用子模块的参数、全程数据、文件输入/输出操作等局部数据结构:数据类型说明、初始化、缺省值、上溢下溢的地址等路径测试:对每一条独立执行路径测试,保证每条语句至少执行一次错误处理测试:检查模块的错误处理功能是否包含有错误或缺陷。是否拒绝不合理的输入、输出的报错信息是否难以理解、是否对错误定位有误、 是否出错原因有误、报错代码处理执行之前,是否已引起系统报错测试人员:开发人员测试方法:白盒测试(基本路径测试、语句覆盖、条件覆盖、判定覆盖、判定条件覆盖、条件组合覆盖、路径覆盖等)单测目的:- 提高代码质量。之前在做测试时候,在项目启动后,通过Swagger或Postman访问接口进行测试,修改代码之后每次都要重新启动,耗时较长- 方便重构。重构之后如果不确定代码的正确性,可以走一遍单元测试做参考- 开发阶段排除Bug的例子:调度编辑功能,在ApplicationService层实现逻辑是参数校验、调度删除、调度新增;其中,在新增时候也有做校验,且校验失败时候直接抛出异常;导致我在新增正常调度成功后,编辑错误数据,删除成功了,但重新添加时候失败;详细测试数据见Spring Boot项目单元测试及集成测试的应用单元的**2.2 Application-Service层 **示例
  2. 集成测试集成测试是在单元测试的基础上,将所有已通过单元测试的模块按照概要设计的要求组装为子系统或系统,并进行测试的过程。目标是把通过了单元测试的模块拿来,构造一个在设计中所描述的程序结构,应当避免一次性的集成(除非软件规模很小),而采用增量集成。测试阶段:单元测试通过后测试对象:模块间的接口测试内容:模块之间数据传输、模块之间功能冲突、模块组装功能正确性、全局数据结构、单模块缺陷对系统的影响测试人员:开发人员测试方法:白盒测试+黑盒测试
  3. 系统测试测试阶段:集成测试通过后测试对象:整个系统测试内容:功能、界面、可靠性、易用性、性能、兼容性、安全性等测试人员:测试人员测试方法:黑盒测试
  4. 验收测试验收测试是部署软件之前的最后一个测试操作。它是技术测试的最后一个阶段,也称为交付测试。验收测试的目的是确保软件准备就绪,按照项目合同、任务书、双方约定的验收依据文档,向软件购买都展示该软件系统满足原始需求。测试阶段:系统测试通过后测试对象:整个系统测试内容:同系统测试测试人员:需求方或最终用户测试方法:黑盒测试测试阶段对比图如下:集成测试单元测试系统测试验收测试测试阶段单元测试通过后编码后或编码前集成测试通过后系统测试通过后测试对象模块间的接口最小模块整个系统整个系统测试内容模块之间数据传输、模块之间功能冲突、 模块组装功能正确性、全局数据结构、单模块缺陷对系统的影响模块接口测试、局部数据结构、路径测试、错误处理测试、边界测试功能、界面、可靠性、易用性、性能、兼容性、安全性等同系统测试测试人员开发人员开发人员测试人员需求方或最终用户测试方法白盒测试+黑盒测试白盒测试黑盒测试黑盒测试

单元测试

  1. Junit手册:JUnit API常用注解:@BeforeClass: 在所有测试方法执行前执行一次,一般在其中写上整体初始化的代码public class Example { @BeforeClass public static void onlyOnce() { ... } @Test public void one() { ... } @Test public void two() { ... } }@Before:在每个方法测试前执行,一般用来初始化方法 public class Example { List empty; @Before public void initialize() { empty= new ArrayList(); } @Test public void size() { ... } @Test public void remove() { ... } }@AfterClass:在所有测试方法后执行一次,一般在其中写上销毁和释放资源的代码public class Example { private static DatabaseConnection database; @BeforeClass public static void login() { database= ...; } @Test public void something() { ... } @Test public void somethingElse() { ... } @AfterClass public static void logout() { database.logout(); } }@After:在每个测试方法执行后,在方法执行完成后要做的事情public class Example { File output; @Before public void createOutputFile() { output= new File(...); } @Test public void something() { ... } @After public void deleteOutputFile() { output.delete(); } }@Test:编写一般测试用例。支持两个可选参数,用于异常测试和限制测试执行时间public class Example { @Test public void method() { org.junit.Assert.assertTrue( new ArrayList().isEmpty() ); } }@Test(expected = Exception.class) :测试方法期望得到的异常类,如果方法执行没有抛出指定的异常,则测试失败 @Test(expected=IndexOutOfBoundsException.class) public void outOfBounds() { new ArrayList<Object>().get(1); }@Test(timeout = m):测试方法花费的时间超过指定的时间m毫秒后,将测试失败 @Test(timeout=100) public void infinity() { while(true); }@Ignore("not ready yet"):执行测试时将忽略掉此方法,如果用于修饰类,则忽略整个类 @Ignore public class IgnoreMe { @Test public void test1() { ... } @Test public void test2() { ... } }
  2. MockitoMock测试即在测试过程中,对于一些不容易构造的,与本次单元测试关系不大但又有上下文依赖关系的对象,用一个虚拟的对象(Mock对象)来模拟,以便单元测试能够进行。Mockito是Java单元测试的mock开源框架。手册:mockito-core 3.11.2 javadoc (org.mockito)常用方法:mock对象@BeforeEachvoid init() { scheduler = mock(Scheduler.class); scheduleService = new ScheduleServiceImpl(scheduler);}参数匹配:anyInt()、anyLong()、anyFloat()、anyDouble(),匹配任意xx类型数据@Testvoid testMoc(){ ArrayList test = mock(ArrayList.class); when(test.get(anyInt())).thenReturn("mock argumentMatcher"); System.out.println(test.get(0));}设置对象调用的预期返回值:when(condition).thenReturn(value1); 当满足when中条件时候,返回value1;当不满足时候,返回nullwhen(condition).thenReturn(value1).thenReturn(value2); 当满足when中条件时候,第一次返回value1,第n(n>1且∈N*)次返回value2when(condition).thenReturn(value1)...thenReturn(valuen); 当满足when中条件时候,第一次返回value1,第二次返回value2...第n(n>1且∈N*)次返回valuendoReturn().when().someMethod(); 与thenReturn()的区别是不会调用真实方法示例如下:当满足when中条件时候,返回value1:@Testvoid mockitoMethodWhenThen(){ ArrayList mockList = mock(ArrayList.class); //参数匹配(when中条件不满足时候,renturn null) when(mockList.get(anyInt())).thenReturn("mock argumentMatcher"); System.out.println(mockList.get(0));}当不满足条件时候,返回null:@Testvoid mockitoMethodWhenThen(){ ArrayList mockList = mock(ArrayList.class); //参数匹配(when中条件不满足时候,renturn null) when(mockList.get(1)).thenReturn("mock argumentMatcher"); System.out.println(mockList.get(0));}当满足when中条件时候,第一次返回value1,第n次(n>1且∈N*)返回value2:@Test void mockitoMethodWhenThen(){ ArrayList mockList = mock(ArrayList.class); //参数匹配(when中条件不满足时候,renturn null) when(mockList.get(anyInt())).thenReturn("First trigger").thenReturn("Second trigger").thenReturn("Third trigger"); System.out.println(mockList.get(0)); System.out.println(mockList.get(1)); System.out.println(mockList.get(2)); System.out.println(mockList.get(2)); System.out.println(mockList.get(3)); }@Testvoid mockitoMethodDoReturn() { ArrayList mockList = mock(ArrayList.class); mockList.add(1); doReturn(mockList.add(2)).when(mockList).add(3); mockList.add(3); assertThat(mockList.get(2)).isEqualTo(2);}API如下所示:/** * Enables stubbing methods. Use it when you want the mock to return particular value when particular method is called. * <p> * Simply put: "<b>When</b> the x method is called <b>then</b> return y". * * <p> * Examples: * * <pre class="code"><code class="java"> * <b>when</b>(mock.someMethod()).<b>thenReturn</b>(10); * * //you can use flexible argument matchers, e.g: * when(mock.someMethod(<b>anyString()</b>)).thenReturn(10); * * //setting exception to be thrown: * when(mock.someMethod("some arg")).thenThrow(new RuntimeException()); * * //you can set different behavior for consecutive method calls. * //Last stubbing (e.g: thenReturn("foo")) determines the behavior of further consecutive calls. * when(mock.someMethod("some arg")) * .thenThrow(new RuntimeException()) * .thenReturn("foo"); * * //Alternative, shorter version for consecutive stubbing: * when(mock.someMethod("some arg")) * .thenReturn("one", "two"); * //is the same as: * when(mock.someMethod("some arg")) * .thenReturn("one") * .thenReturn("two"); * * //shorter version for consecutive method calls throwing exceptions: * when(mock.someMethod("some arg")) * .thenThrow(new RuntimeException(), new NullPointerException(); * * </code></pre> * * For stubbing void methods with throwables see: {@link Mockito#doThrow(Throwable...)} * <p> * Stubbing can be overridden: for example common stubbing can go to fixture * setup but the test methods can override it. * Please note that overridding stubbing is a potential code smell that points out too much stubbing. * <p> * Once stubbed, the method will always return stubbed value regardless * of how many times it is called. * <p> * Last stubbing is more important - when you stubbed the same method with * the same arguments many times. * <p> * Although it is possible to verify a stubbed invocation, usually <b>it's just redundant</b>. * Let's say you've stubbed <code>foo.bar()</code>. * If your code cares what <code>foo.bar()</code> returns then something else breaks(often before even <code>verify()</code> gets executed). * If your code doesn't care what <code>get(0)</code> returns then it should not be stubbed. * * <p> * See examples in javadoc for {@link Mockito} class * @param methodCall method to be stubbed * @return OngoingStubbing object used to stub fluently. * <strong>Do not</strong> create a reference to this returned object. */@CheckReturnValuepublic static <T> OngoingStubbing<T> when(T methodCall) { return MOCKITO_CORE.when(methodCall);}``````/** * Sets a return value to be returned when the method is called. E.g: * <pre class="code"><code class="java"> * when(mock.someMethod()).thenReturn(10); * </code></pre> * * See examples in javadoc for {@link Mockito#when} * * @param value return value * * @return object that allows stubbing consecutive calls */OngoingStubbing<T> thenReturn(T value);``````/** * Sets consecutive return values to be returned when the method is called. E.g: * <pre class="code"><code class="java"> * when(mock.someMethod()).thenReturn(1, 2, 3); * </code></pre> * * Last return value in the sequence (in example: 3) determines the behavior of further consecutive calls. * <p> * See examples in javadoc for {@link Mockito#when} * * @param value first return value * @param values next return values * * @return object that allows stubbing consecutive calls */// Additional method helps users of JDK7+ to hide heap pollution / unchecked generics array// creation warnings (on call site)@SuppressWarnings({"unchecked", "varargs"})OngoingStubbing<T> thenReturn(T value, T... values);验证调用次数:verify(T mock);verify(T mock, VerificationMode mode);@Testvoid mockitoMethodVerify(){ ArrayList mockList = mock(ArrayList.class); //验证方法是否被调用及执行次数 mockList.add(1); verify(mockList).add(1); mockList.add(1); mockList.add(2); verify(mockList,times(2)).add(1); verify(mockList,never()).add(3); verify(mockList,atLeastOnce()).add(1); verify(mockList,atLeast(2)).add(1); verify(mockList,atMostOnce()).add(2); verify(mockList,atMost(2)).add(1);}@Test void mockitoMethodVerify(){ ArrayList mockList = mock(ArrayList.class); //验证方法是否被调用及执行次数 mockList.add(1); mockList.add(1); verify(mockList,atLeast(3)).add(1); }API如下所示:/** * Verifies certain behavior <b>happened once</b>. * <p> * Alias to <code>verify(mock, times(1))</code> E.g: * <pre class="code"><code class="java"> * verify(mock).someMethod("some arg"); * </code></pre> * Above is equivalent to: * <pre class="code"><code class="java"> * verify(mock, times(1)).someMethod("some arg"); * </code></pre> * <p> * Arguments passed are compared using <code>equals()</code> method. * Read about {@link ArgumentCaptor} or {@link ArgumentMatcher} to find out other ways of matching / asserting arguments passed. * <p> * Although it is possible to verify a stubbed invocation, usually <b>it's just redundant</b>. * Let's say you've stubbed <code>foo.bar()</code>. * If your code cares what <code>foo.bar()</code> returns then something else breaks(often before even <code>verify()</code> gets executed). * If your code doesn't care what <code>foo.bar()</code> returns then it should not be stubbed. * * <p> * See examples in javadoc for {@link Mockito} class * * @param mock to be verified * @return mock object itself */@CheckReturnValuepublic static <T> T verify(T mock) { return MOCKITO_CORE.verify(mock, times(1));}``````/** * Verifies certain behavior happened at least once / exact number of times / never. E.g: * <pre class="code"><code class="java"> * verify(mock, times(5)).someMethod("was called five times"); * * verify(mock, atLeast(2)).someMethod("was called at least two times"); * * //you can use flexible argument matchers, e.g: * verify(mock, atLeastOnce()).someMethod(<b>anyString()</b>); * </code></pre> * * <b>times(1) is the default</b> and can be omitted * <p> * Arguments passed are compared using <code>equals()</code> method. * Read about {@link ArgumentCaptor} or {@link ArgumentMatcher} to find out other ways of matching / asserting arguments passed. * <p> * * @param mock to be verified * @param mode times(x), atLeastOnce() or never() * * @return mock object itself */@CheckReturnValuepublic static <T> T verify(T mock, VerificationMode mode) { return MOCKITO_CORE.verify(mock, mode);}``````/** * Allows verifying exact number of invocations. E.g: * <pre class="code"><code class="java"> * verify(mock, times(2)).someMethod("some arg"); * </code></pre> * * See examples in javadoc for {@link Mockito} class * * @param wantedNumberOfInvocations wanted number of invocations * * @return verification mode */@CheckReturnValuepublic static VerificationMode times(int wantedNumberOfInvocations) { return VerificationModeFactory.times(wantedNumberOfInvocations);}``````/** * Alias to <code>times(0)</code>, see {@link Mockito#times(int)} * <p> * Verifies that interaction did not happen. E.g: * <pre class="code"><code class="java"> * verify(mock, never()).someMethod(); * </code></pre> * * <p> * If you want to verify there were NO interactions with the mock * check out {@link Mockito#verifyZeroInteractions(Object...)} * or {@link Mockito#verifyNoMoreInteractions(Object...)} * <p> * See examples in javadoc for {@link Mockito} class * * @return verification mode */@CheckReturnValuepublic static VerificationMode never() { return times(0);}``````/** * Allows at-least-once verification. E.g: * <pre class="code"><code class="java"> * verify(mock, atLeastOnce()).someMethod("some arg"); * </code></pre> * Alias to <code>atLeast(1)</code>. * <p> * See examples in javadoc for {@link Mockito} class * * @return verification mode */@CheckReturnValuepublic static VerificationMode atLeastOnce() { return VerificationModeFactory.atLeastOnce();}``````/** * Allows at-least-x verification. E.g: * <pre class="code"><code class="java"> * verify(mock, atLeast(3)).someMethod("some arg"); * </code></pre> * * See examples in javadoc for {@link Mockito} class * * @param minNumberOfInvocations minimum number of invocations * * @return verification mode */@CheckReturnValuepublic static VerificationMode atLeast(int minNumberOfInvocations) { return VerificationModeFactory.atLeast(minNumberOfInvocations);}``````/** * Allows at-most-once verification. E.g: * <pre class="code"><code class="java"> * verify(mock, atMostOnce()).someMethod("some arg"); * </code></pre> * Alias to <code>atMost(1)</code>. * <p> * See examples in javadoc for {@link Mockito} class * * @return verification mode */@CheckReturnValuepublic static VerificationMode atMostOnce() { return VerificationModeFactory.atMostOnce();}``````/** * Allows at-most-x verification. E.g: * <pre class="code"><code class="java"> * verify(mock, atMost(3)).someMethod("some arg"); * </code></pre> * * See examples in javadoc for {@link Mockito} class * * @param maxNumberOfInvocations max number of invocations * * @return verification mode */@CheckReturnValuepublic static VerificationMode atMost(int maxNumberOfInvocations) { return VerificationModeFactory.atMost(maxNumberOfInvocations);}验证失败对应的异常:异常抛出when() ...thenThrow(); 当满足条件时候,抛出异常doThrow()...when(); 满足when中条件时候,抛出异常当when条件中函数返回值为void时候,不可用thenThrow(),用doThrow()@Testvoid mockitoMethodException() { ArrayList mockList = mock(ArrayList.class); mockList.add(1); when(mockList.add(2)).thenThrow(new Exception("异常抛出!")); doThrow(new Exception("异常抛出!")).when(mockList).clear();}返回回调接口生成期望值when(methodCall).thenAnswer(answer));doAnswer(answer).when(methodCall).[method];调用真实的方法doCallRealMethod().when(mock).[method];
  3. AssertJAssertJ: JAVA 流式断言器,常见的断言器一条断言语句只能对实际值断言一个校验点,而流式断言器,支持一条断言语句对实际值同时断言多个校验点。是用来验证输出和期望是否一致的一个工具。手册:assertj-core 3.20.2 javadoc (org.assertj)常用方法:1. as(): 添加错误提示信息assertThat("abc").as("校验abc").isEqualTo("abcd");1. isEqualsTo():相等2. contains():包含assertThat("abc").as("校验abc").isEqualTo("abc").contains("d");1. isNull() / isNotNull():为空判断Object object = null;assertThat(object).isNotNull();assertThat(object).isNull();1. isIn() / isNotIn():范围判断List list = new ArrayList();assertThat(list).isIn(new ArrayList(), new HashMap());assertThat(list).isNotIn(new ArrayList(), new HashMap());1. hasSize():大小校验List list = new ArrayList();assertThat(list).hasSize(1);1. startsWith() / endsWith()等等...可参考API

集成测试

Spring Boot项目单元测试及集成测试的应用

  1. 搭建测试环境Junit:Java应用程序单元测试标准类库Spring Test & Spring Boot Test:Spring Boot 应用程序功能集成化测试支持AssertJ:一个轻量级断言类库Hamcrest:一个对象匹配器类库,用Junit自带的即可Mockito: 一个java Mock测试框架JSONassert:一个用于JSON的断言库JsonPath: 一个Json操作类库手册:Junit:JUnit APIAssertJ:assertj-core 3.20.2 javadoc (org.assertj)Mockito:mockito-core 3.11.2 javadoc (org.mockito)Spring Boot Test:Spring Boot Reference Documentation
  2. 集成测试demo2.1 Endpoint层@SpringBootTest(classes = ScheduleSetup.class)@AutoConfigureMockMvcclass ScheduleEndpointTest { @Autowired private MockMvc mockMvc; private ScheduleService scheduleService; @BeforeEach void initScheduleService() { scheduleService = mock(ScheduleServiceImpl.class); } @Test void add_schedule() { try { System.out.println("scheduleService>>>" + scheduleService); //构造请求数据 Map<String, Integer> startPolicyParams = new HashMap<>(); ScheduleDTO scheduleDTO = ScheduleDTO.builder().name("单元测试-1").owner("lijialin").severity(Severity.HIGH) .description("hah").startPolicyType(StartPolicyType.ONCE).startPolicyParams(null) .startTime(System.currentTimeMillis()).paramData(null).createBy("lijialin") .build(); //模拟post请求 mockMvc.perform(MockMvcRequestBuilders.post("/v1/soar-common/schedules") .content(JsonUtil.toJson(scheduleDTO)) .contentType(MediaType.APPLICATION_JSON)) /*.andExpect(MockMvcResultMatchers.status().isOk())*/; assertThat(scheduleService.existsByName("单元测试-1")); } catch (Exception e) { e.printStackTrace(); } }2.2 Application-Service层class ScheduleApplicationServiceTest { private ScheduleApplicationService applicationService; private ScheduleService scheduleService; @BeforeEach void init() { applicationService = new ScheduleApplicationService(mock(ScheduleServiceImpl.class), mock(ScheduleRecordServiceImpl.class)); scheduleService = mock(ScheduleServiceImpl.class); } @Test void edit() { Map<String, Integer> paramsMap = new HashMap<>(); paramsMap.put("week", 6); paramsMap.put("hour", 8); paramsMap.put("minute", 12); paramsMap.put("interval", 1); ScheduleDTO scheduleDTO = ScheduleDTO.builder().name("新增调度1") .createBy("lijialin") .severity(Severity.HIGH) .startTime(1646387494000L) .startPolicyType(StartPolicyType.WEEKLY) .endPolicyType(EndPolicyType.END_TIME) .endPolicyValue(1646964300000L) .startPolicyParams(paramsMap) .build(); applicationService.save(scheduleDTO); assertThat(scheduleService.existsByName("新增调度1")); ScheduleEditDTO scheduleEditDTO = ScheduleEditDTO.builder() .id("37f62f73-65fc-4c00-89ff-eff295f784bf") .name("编辑调度1") .createBy("lijialin") .severity(Severity.HIGH) .startTime(1646387494000L) .startPolicyType(StartPolicyType.WEEKLY) .endPolicyType(EndPolicyType.END_TIME) .endPolicyValue(1646784300000L) .startPolicyParams(paramsMap) .build(); applicationService.edit(scheduleEditDTO); assertThat(scheduleService.existsByName("编辑调度1")); }}2.3 Domain-Service层@Slf4jclass ScheduleServiceImplTest { private ScheduleService scheduleService; private Scheduler scheduler; @BeforeEach void init() { scheduler = mock(Scheduler.class); scheduleService = new ScheduleServiceImpl(scheduler); } @Test void addSchedule() { Map<String, Object> cornParams = new HashMap<>(); cornParams.put("type", "ONCE"); String cornParamsStr = JsonUtil.toJson(cornParams); ScheduleObject scheduleObject = ScheduleObject.builder().uuid("72bc5a2d-e5ee-4e83-9866-32b0df54bdb2") .name("单元测试-6") .creator("lijialin") .owner("lijialin") .type("ONCE") .severity(3) .description("desc...") .startTime(System.currentTimeMillis()) .cornParams(cornParamsStr) .build(); scheduleService.addSchedule(scheduleObject); assertThat(scheduleService.existsByName("单元测试-6")); }

本文转载自: https://blog.csdn.net/Yingtaozi_0/article/details/123323395
版权归原作者 Rover.x 所有, 如有侵权,请联系我们删除。

“SpringBoot项目中单元测试与集成测试的应用”的评论:

还没有评论