当前位置: 首页 > 科技观察

Java如何优雅地实现单元测试与集成测试

时间:2023-03-20 11:44:53 科技观察

Java如何优雅的实现单元测试和集成测试?转载请联系无敌码农公众号。在日常的开发过程中,为了保证代码的质量,有抱负的程序员一般都会对自己编写的代码进行全面的测试。这种测试不仅体现在对普通函数的简单接口调用上,还基于中的各个逻辑分支,对主要逻辑进行尽可能多的覆盖单元测试和集成测试。对于程序员来说,上面提到的测试不仅仅依赖于Postman等网络工具,而是通过编写独立的单元/集成测试代码来实现的。具体来说,在Java中,是基于JUnit、Mocktio等测试框架编写相应的UT和IT代码,并在此过程中,提前发现软件bug,对编写的代码进行重新审视和优化。说实话,编写测试代码是提高软件质量和自身编程水平的一种非常有用的手段。但是在工作中,并不是每个人都能正确掌握单元测试和集成测试代码的编写和组织。以Maven项目代码为例,很多人会混淆单元测试和集成测试代码,导致大多数Maven项目代码:“mvntest”几乎难以跑通。本文想表达的是如何有效区分和组织Maven项目中的单元测试和集成测试代码,使它们互不干扰,并具体演示它们是如何编写的。Maven测试代码结构的组织我们知道Maven项目结构中的“src/test”目录是专门用来存放测试代码的,但是痛苦的是Maven的标准目录结构只定义了这样一个测试目录,也就是说它本身无法区分单元测试代码和集成测试代码,这也是为什么很多人把UT和IT代码同时写到“src/test”目录下,导致“mvntest”难以运行的原因。那么有没有什么办法友好的解决这个问题呢?在接下来的内容中,我们将以使用Maven构建一个SpringBoot项目为例,来演示如何在Maven中分离UT和IT。具体步骤如下:1)、首先我们创建一个基于Maven的SpringBoot项目。代码结构如下图所示:如上图所示,在规划的目录结构中,我们将“src/integration-test”目录下的IT代码目录和资源文件目录分开,一般情况下,默认“仍然使用“src/test”目录作为存放UT代码的目录,Maven在构建过程中默认只运行UT代码。这样即使IT代码因为网络、环境等原因不能正常执行,也不会影响UT代码的运行。2)创建一个区分UT和IT代码的MavenProfiles文件。默认情况下,Maven无法主动识别“src/test”目录之外的测试代码,所以当我们将IT代码抽象到“src/integration-test”目录后,需要通过编写MavenProfiles文件来区分。具体示意图如下:如上图所示,我们可以在“src”目录下并行创建一个“profiles”目录,分别使用“dev”和“integration-test”中的config.properties文件目录用来区分,dev目录下的config.properties文件内容为:profile=devintegration-test目录下的config.properties文件内容为:profile=integration-test3),通过pom的.xml文件配置上述profiles的生效规则为了让这些profiles生效,我们还需要在pom.xml文件中进行相应的配置。具体如下:devtruedevtruefalseintegration-testintegration-testfalse以上内容首先定义了区分dev和integration-test环境的profile信息,然后在build标签中定义了资源信息和相关插件,如下:${project.artifactId}profiles/${build.profile.id}/config.propertiesfalsesrc/main/java**/*.properties**/*.xml**/*.tld**/*.ymltruesrc/main/resources**/*.properties**/*.xml**/*.tld**/*.yml<include>**/*.shorg.springframework.bootspring-boot-maven-pluginorg.codehaus.mojobuild-helper-maven-plugin3.1.0add-integration-test-sourcesgenerate-test-sourcesadd-test-sourcesrc/integration-目录test/javaadd-integration-test-resourcesgenerate-test-resourcesadd-test-resource<配置>truesrc/integration-test/resources**/*.propertiesorg.apache.maven。插件maven-surefire-plugin2.18${skip.unit.tests}**/IT*.javaorg.apache.maven.pluginsmaven-failsafe-plugin2.18集成测试集成测试验证${skip.integration.tests}到这里我们就完成了基于Maven构建的SpringBoot项目的UT和IT代码目录的分离配置。此时UT代码的执行仍然是通过默认的“mvntest”命令,集成测试代码的运行可以通过如下命令运行:mvncleanverify-Pintegration-test单元测试代码示例通过前面的配置操作,完成单元测试和集成测试代码目录的分离。在后续的开发过程中,只需要在相应的测试目录下编写相应的测试代码即可。接下来我们模拟一段业务逻辑,演示如何编写其对应的UT代码。具体如下:如上图所示,参考MVC三层规范,我们编写了一个接口逻辑。接口controller层调用Service层处理Http请求,Service层在处理逻辑时调用Dao层操作数据库,将具体信息插入数据库。那么我们写单元测试(UT)代码的时候,是针对单个逻辑单元的测试,而不是从头到尾的整个逻辑。它的运行不应该依赖于任何网络环境或其他组件,所有依赖的组件或网络都应该首先被Mocked。以单元测试TestServceImpl中的“saveTest”方法为例,UT代码写法如下:;@MockBeanTestDaotestDao;@TestpublicvoidsaveTest(){//调用测试方法testServiceImpl.saveTest("无敌码农微信公众号");//验证执行测试逻辑中是否调用了addUser方法verify(testDao).addUser(任何());}}如上UT代码所示,我们UT测试的主要对象是TestServiceImpl类,所以可以在@SpringBootTest注解中指定作用域。而@ActiveProfiles("test")表示代码依赖的系统参数,可以从test资源目录下的resources/application-test.yml文件中获取。单元测试的主要目的是验证单元代码中的逻辑。对于依赖的数据库Dao组件,不在测试范围内,但是如果没有Dao组件对象,UT代码在执行过程中也会报错,所以一般会通过@MockBean注解组件Mock来解决代码UT测试过程中的依赖问题。这时运行“mvntest”命令:单元测试代码执行正常!集成测试代码示例的编写方式类似于SpringBoot中的IT代码,但其执行范围包括整个上下文。下面我们模拟从Controller层发起的Http接口请求为例,完整测试整个接口的逻辑,最后将数据存入数据库。具体测试代码如下:@RunWith(SpringRunner.class)@SpringBootTest@ActiveProfiles("test")publicclassITTestControllerTest{@AutowiredTestControllertestController;@TestpublicvoidsaveTest(){testController.saveTest("无敌码农微信公众号");}}可以看出,只要集成测试代码没有在@SpringBootTest中指定具体的类,其默认的执行范围就是整个应用的上下文。由于代码中会为依赖的组件启动整个应用上下文,所以依赖不会报错,可以理解为一个正常启动的SpringBoot应用。需要注意的是,由于IT代码目录有独立的资源配置,相关的依赖配置,比如数据库,需要在“src/integration-test/resouces/application-test.yml”文件中单独配置,例如:spring:application:name:springboot-test-demo#Database逻辑datasource:url:jdbc:mysql://127.0.0.1:3306/testusername:rootpassword:123456type:com.alibaba.druid.pool.DruidDataSourcedriver-class-name:com。mysql.jdbc.Driverseparator://server:port:8080此时运行集成测试命令“mvncleanverify-Pintegration-test”:可以看到IT测试代码执行正常!后记本文重点介绍Java项目工程实践中如何编写单元测试(UT)和集成测试(IT)代码。在日常写代码的过程中,写好测试代码是一个非常好的习惯。一般来说,对于错误项目的UT或IT代码执行,要求严格的团队会使其在构建过程中失败。对团队成员严格要求。原文链接:https://mp.weixin.qq.com/s/RT-KKT1BskUYEvYAXhms5A