<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Java如何優(yōu)雅地實(shí)現(xiàn)單元測(cè)試與集成測(cè)試

          共 6504字,需瀏覽 14分鐘

           ·

          2020-07-21 17:13




          0cfbe8a4aea4476df9dcbde8507ba585.webp


          在日常的開(kāi)發(fā)過(guò)程中,為了保證代碼質(zhì)量,有追求的程序員一般都會(huì)對(duì)自己編寫(xiě)的代碼進(jìn)行充分的測(cè)試,這種測(cè)試不僅僅是體現(xiàn)在對(duì)正常功能的簡(jiǎn)單接口調(diào)用,而是要根據(jù)代碼中的各種邏輯分支,進(jìn)行盡可能多的覆蓋性單元測(cè)試以及主要邏輯的集成測(cè)試。


          上面說(shuō)到的測(cè)試對(duì)于程序員來(lái)說(shuō),絕不僅僅只是依賴(lài)于Postman之類(lèi)的網(wǎng)絡(luò)工具,而要以編寫(xiě)?yīng)毩⒌膯卧?集成測(cè)試代碼的方式來(lái)實(shí)現(xiàn),具體來(lái)說(shuō)在Java中就是要基于JUnit、Mocktio之類(lèi)的測(cè)試框架編寫(xiě)相應(yīng)的UT及IT代碼,并在這個(gè)過(guò)程中提前發(fā)現(xiàn)軟件Bug、重新審視所寫(xiě)代碼并進(jìn)行優(yōu)化。


          實(shí)話(huà)說(shuō)編寫(xiě)測(cè)試代碼對(duì)提高軟件質(zhì)量,及自身編程水平來(lái)說(shuō)都是一種非常有用的手段。但在工作中,并不是所有人都能正確地掌握單元測(cè)試和集成測(cè)試代碼的寫(xiě)法和組織形式。以Maven工程代碼為例,很多人會(huì)把單元測(cè)試和集成測(cè)試代碼弄混,這樣導(dǎo)致的后果就是大部分Maven工程代碼:"mvn test"幾乎很難跑通。


          而本文想要表達(dá)的內(nèi)容就是如何在Maven工程中有效的區(qū)分和組織單元測(cè)試、集成測(cè)試代碼使得它們互不干擾,并具體演示它們的寫(xiě)法。



          Maven測(cè)試代碼結(jié)構(gòu)的組織

          d589e5eb723115ddb581410af1810f3d.webp


          我們知道在Maven工程結(jié)構(gòu)中“src/test”目錄是專(zhuān)門(mén)用于存放測(cè)試代碼的,但令人痛苦的是Maven的標(biāo)準(zhǔn)目錄結(jié)構(gòu)只定義了這樣一個(gè)測(cè)試目錄,也就是說(shuō)它本身是無(wú)法單獨(dú)區(qū)分單元測(cè)試代碼和集成測(cè)試代碼的,這也是為什么很多人會(huì)把UT和IT代碼同時(shí)寫(xiě)到"src/test"目錄而導(dǎo)致“mvn test”難以跑過(guò)的原因。


          那么有什么辦法可以友好地解決這個(gè)問(wèn)題呢?在接下來(lái)的內(nèi)容中我們以Maven構(gòu)建Spring Boot項(xiàng)目為例來(lái)具體演示下在Maven中如何友好地分離UT及IT,具體步驟如下:


          1)、首先我們創(chuàng)建一個(gè)基于Maven構(gòu)建的Spring Boot項(xiàng)目,代碼結(jié)構(gòu)如下圖所示:


          9fd83cceec2f3e12d358e09d76b2eec5.webp


          如上圖所示,在規(guī)劃的目錄結(jié)構(gòu)中我們將IT的代碼目錄及資源文件目錄單獨(dú)分離在“src/integration-test”目錄下,默認(rèn)的“src/test”目錄還是作為存放UT代碼的目錄,而Maven在構(gòu)建的過(guò)程中默認(rèn)只運(yùn)行UT代碼。這樣即便IT代碼由于網(wǎng)絡(luò)、環(huán)境等原因無(wú)法正常執(zhí)行,但也不至于影響到UT代碼的運(yùn)行。


          2)、創(chuàng)建區(qū)分UT、IT代碼的Maven Profiles文件


          默認(rèn)情況下Maven是無(wú)法主動(dòng)識(shí)別“src/test”目錄之外的測(cè)試代碼的,所以當(dāng)我們將IT代碼抽象到"src/integration-test"目錄之后,需要通過(guò)編寫(xiě)Maven Profiles文件來(lái)進(jìn)行區(qū)分,具體示意圖如下:


          cf9fca556670561a5bdcbfa08f34ad78.webp


          如上圖所示,我們可以在與“src”目錄平行創(chuàng)建一個(gè)“profiles”的目錄,其中分別用“dev”“integration-test”目錄中的config.properties文件來(lái)進(jìn)行區(qū)分,其中dev目錄下的config.properties文件的內(nèi)容為:

          profile=dev

          而integration-test目錄中的config.properties文件則為:

          profile=integration-test


          3)、通過(guò)pom.xml文件配置上述profiles文件生效規(guī)則


          為了使得這些profiles文件生效,我們還需要在pom.xml文件中進(jìn)行相應(yīng)的配置。具體如下:


          <profiles>
          ????
          ????<profile>
          ????????<id>devid>
          ????????<activation>
          ????????????<activeByDefault>trueactiveByDefault>
          ????????activation>
          ????????<properties>
          ????????????<build.profile.id>devbuild.profile.id>
          ????????????
          ????????????<skip.integration.tests>trueskip.integration.tests>
          ????????????<skip.unit.tests>falseskip.unit.tests>
          ????????properties>
          ????profile>
          ????
          ????<profile>
          ????????<id>integration-testid>
          ????????<properties>
          ????????????<build.profile.id>integration-testbuild.profile.id>
          ????????????
          ????????????<skip.integration.tests>falseskip.integration.tests>
          ????????????<skip.unit.tests>trueskip.unit.tests>
          ????????properties>
          ????profile>
          profiles>


          上述內(nèi)容先定義了區(qū)分dev及integration-test環(huán)境的的profile信息,接下來(lái)在build標(biāo)簽中定義資源信息及相關(guān)plugin,具體如下:

          <build>
          ????<finalName>${project.artifactId}finalName>
          ????
          ????<filters>
          ????????<filter>profiles/${build.profile.id}/config.propertiesfilter>
          ????filters>
          ????<resources>
          ????????<resource>
          ????????????<filtering>falsefiltering>
          ????????????<directory>src/main/javadirectory>
          ????????????<includes>
          ????????????????<include>**/*.propertiesinclude>
          ????????????????<include>**/*.xmlinclude>
          ????????????????<include>**/*.tldinclude>
          ????????????????<include>**/*.ymlinclude>
          ????????????includes>
          ????????resource>
          ????????
          ????????<resource>
          ????????????<filtering>truefiltering>
          ????????????<directory>src/main/resourcesdirectory>
          ????????????<includes>
          ????????????????<include>**/*.propertiesinclude>
          ????????????????<include>**/*.xmlinclude>
          ????????????????<include>**/*.tldinclude>
          ????????????????<include>**/*.ymlinclude>
          ????????????????<include>**/*.shinclude>
          ????????????includes>
          ????????resource>
          ????resources>
          ????<plugins>
          ????????<plugin>
          ????????????<groupId>org.springframework.bootgroupId>
          ????????????<artifactId>spring-boot-maven-pluginartifactId>
          ????????plugin>
          ????????
          ????????<plugin>
          ????????????<groupId>org.codehaus.mojogroupId>
          ????????????<artifactId>build-helper-maven-pluginartifactId>
          ????????????<version>3.1.0version>
          ????????????<executions>
          ????????????????
          ????????????????<execution>
          ????????????????????<id>add-integration-test-sourcesid>
          ????????????????????<phase>generate-test-sourcesphase>
          ????????????????????<goals>
          ????????????????????????<goal>add-test-sourcegoal>
          ????????????????????goals>
          ????????????????????<configuration>
          ????????????????????????
          ????????????????????????<sources>
          ????????????????????????????<source>src/integration-test/javasource>
          ????????????????????????sources>
          ????????????????????configuration>
          ????????????????execution>
          ????????????????
          ????????????????<execution>
          ????????????????????<id>add-integration-test-resourcesid>
          ????????????????????<phase>generate-test-resourcesphase>
          ????????????????????<goals>
          ????????????????????????<goal>add-test-resourcegoal>
          ????????????????????goals>
          ????????????????????<configuration>
          ????????????????????????
          ????????????????????????<resources>
          ????????????????????????????<resource>
          ????????????????????????????????<filtering>truefiltering>
          ????????????????????????????????<directory>src/integration-test/resourcesdirectory>
          ????????????????????????????????<includes>
          ????????????????????????????????????<include>**/*.propertiesinclude>
          ????????????????????????????????includes>
          ????????????????????????????resource>
          ????????????????????????resources>
          ????????????????????configuration>
          ????????????????execution>
          ????????????executions>
          ????????plugin>
          ????????
          ????????<plugin>
          ????????????<groupId>org.apache.maven.pluginsgroupId>
          ????????????<artifactId>maven-surefire-pluginartifactId>
          ????????????<version>2.18version>
          ????????????<configuration>
          ????????????????
          ????????????????<skipTests>${skip.unit.tests}skipTests>
          ????????????????
          ????????????????<excludes>
          ????????????????????<exclude>**/IT*.javaexclude>
          ????????????????excludes>
          ????????????configuration>
          ????????plugin>
          ????????
          ????????<plugin>
          ????????????<groupId>org.apache.maven.pluginsgroupId>
          ????????????<artifactId>maven-failsafe-pluginartifactId>
          ????????????<version>2.18version>
          ????????????<executions>
          ????????????????<execution>
          ????????????????????<id>integration-testsid>
          ????????????????????<goals>
          ????????????????????????<goal>integration-testgoal>
          ????????????????????????<goal>verifygoal>
          ????????????????????goals>
          ????????????????????<configuration>
          ????????????????????????<skipTests>${skip.integration.tests}skipTests>
          ????????????????????configuration>
          ????????????????execution>
          ????????????executions>
          ????????plugin>
          ????plugins>
          build>


          到這里我們就完成了基于Maven構(gòu)建的Spring Boot項(xiàng)目的UT及IT代碼目錄的分離配置,此時(shí)對(duì)UT代碼的執(zhí)行還是通過(guò)默認(rèn)“mvn test”命令,而集成測(cè)試代碼的運(yùn)行則可以通過(guò)如下命令:

          mvn clean verify -P integration-test


          單元測(cè)試代碼示例

          d589e5eb723115ddb581410af1810f3d.webp


          通過(guò)前面的配置操作就完成了單元測(cè)試、集成測(cè)試代碼目錄的分離設(shè)置。在后續(xù)的開(kāi)發(fā)過(guò)程中只需要將相應(yīng)的測(cè)試代碼寫(xiě)在對(duì)應(yīng)的測(cè)試目錄即可。接下來(lái)我們模擬一段業(yè)務(wù)邏輯并演示如何編寫(xiě)其對(duì)應(yīng)的UT代碼。具體如下:


          53bae263c8ca9970b984594fdf444119.webp


          如上圖所示,參考MVC三層規(guī)范,我們編寫(xiě)了一個(gè)接口邏輯,該接口Controller層接收Http請(qǐng)求后調(diào)用Service層進(jìn)行處理,而Service層處理邏輯時(shí)會(huì)調(diào)用Dao層操作數(shù)據(jù)庫(kù),并將具體信息插入數(shù)據(jù)庫(kù)。


          那么我們編寫(xiě)單元測(cè)試(UT)代碼時(shí),針對(duì)的是單獨(dú)的某個(gè)邏輯單元的測(cè)試,而不是從頭到位的整個(gè)邏輯,它的運(yùn)行不應(yīng)該依賴(lài)于任何網(wǎng)絡(luò)環(huán)境或其他組件,所有依賴(lài)的組件或網(wǎng)絡(luò)都應(yīng)該先進(jìn)行Mock。以單元測(cè)試TestServceImpl中的“saveTest”方法為例,其UT代碼編寫(xiě)如下:


          @RunWith(SpringRunner.class)
          @SpringBootTest(classes?=?TestServiceImpl.class)
          @ActiveProfiles("test")
          public?class?TestServiceImplTest?{

          ????@Autowired
          ????TestServiceImpl?testServiceImpl;

          ????@MockBean
          ????TestDao?testDao;

          ????@Test
          ????public?void?saveTest()?{
          ????????//調(diào)用測(cè)試方法
          ????????testServiceImpl.saveTest("無(wú)敵碼農(nóng)微信公眾號(hào)");
          ????????//驗(yàn)證執(zhí)行測(cè)試的邏輯中是否調(diào)用過(guò)addUser方法
          ????????verify(testDao).addUser(any());
          ????}
          }


          如上所示UT代碼,我們UT測(cè)試的主要對(duì)象為T(mén)estServiceImpl類(lèi),所以可以在@SpringBootTest注解中進(jìn)行范圍指定。而@ActiveProfiles("test")則表示代碼中所依賴(lài)的系統(tǒng)參數(shù),可以從測(cè)試資源目錄resouces/application-test.yml文件中獲得。


          單元測(cè)試的主要目的是驗(yàn)證單元代碼內(nèi)的邏輯,對(duì)于所依賴(lài)的數(shù)據(jù)庫(kù)Dao組件并不是測(cè)試的范圍,但是沒(méi)有該Dao組件對(duì)象,UT代碼在執(zhí)行的過(guò)程中也會(huì)報(bào)錯(cuò),所以一般會(huì)通過(guò)@MockBean注解進(jìn)行組件Mock,以此解決UT測(cè)試過(guò)程中的代碼依賴(lài)問(wèn)題。此時(shí)運(yùn)行“mvn test”命令:


          fac17dfea8397854fecd0b07607f5f79.webp


          單元測(cè)試代碼得以正常執(zhí)行!


          集成測(cè)試代碼示例

          d589e5eb723115ddb581410af1810f3d.webp


          在Spring Boot中UT代碼的編寫(xiě)方式與IT代碼類(lèi)似,但是其執(zhí)行范圍是包括了整個(gè)上下文環(huán)境。我們以模擬從Controller層發(fā)起Http接口請(qǐng)求為例,來(lái)完整的測(cè)試整個(gè)接口的邏輯,并最終將數(shù)據(jù)存入數(shù)據(jù)庫(kù)。具體測(cè)試代碼如下:


          @RunWith(SpringRunner.class)
          @SpringBootTest
          @ActiveProfiles("test")
          public?class?ITTestControllerTest?{

          ????@Autowired
          ????TestController?testController;

          ????@Test
          ????public?void?saveTest()?{
          ????????testController.saveTest("無(wú)敵碼農(nóng)微信公眾號(hào)");
          ????}
          }


          可以看到對(duì)于集成測(cè)試代碼在@SpringBootTest中并沒(méi)有指定具體的類(lèi),它的默認(rèn)執(zhí)行范圍為整個(gè)應(yīng)用的上下文環(huán)境。而代碼中的依賴(lài)組件由于整個(gè)應(yīng)用上下文都會(huì)被啟動(dòng),所以依賴(lài)上并不會(huì)報(bào)錯(cuò),可以理解為是一個(gè)正常啟動(dòng)的Spring Boot應(yīng)用。


          需要注意的是由于IT代碼的目錄有獨(dú)立的資源配置,所以相關(guān)的依賴(lài)配置,如數(shù)據(jù)庫(kù)等需要在“src/integration-test/resouces/application-test.yml”文件中單獨(dú)配置,例如:


          spring:
          ??application:
          ????name:?springboot-test-demo
          ??#數(shù)據(jù)庫(kù)邏輯
          ??datasource:
          ????url:?jdbc:mysql://127.0.0.1:3306/test
          ????username:?root
          ????password:?123456
          ????type:?com.alibaba.druid.pool.DruidDataSource
          ????driver-class-name:?com.mysql.jdbc.Driver
          ????separator:?//

          server:
          ??port:?8080


          此時(shí)運(yùn)行集成測(cè)試命令“mvn clean verify -P integration-test”:

          b39c9925551616add8c5f1e2299af728.webp

          可以看到執(zhí)行IT測(cè)試代碼得以正常執(zhí)行!



          后記

          d589e5eb723115ddb581410af1810f3d.webp


          本文著重介紹了在Java項(xiàng)目中如何編寫(xiě)單元測(cè)試(UT)和集成測(cè)試(IT)代碼的工程實(shí)踐。在日常編寫(xiě)代碼的過(guò)程中,良好的測(cè)試代碼編寫(xiě)是一種非常好的習(xí)慣,一般來(lái)說(shuō)對(duì)于UT或IT代碼執(zhí)行錯(cuò)誤的工程,要求嚴(yán)格的團(tuán)隊(duì)會(huì)讓其構(gòu)建的過(guò)程中無(wú)法通過(guò),以此來(lái)嚴(yán)格要求團(tuán)隊(duì)成員。


          希望本文的內(nèi)容能對(duì)你的編碼有所啟發(fā),如果覺(jué)得還不錯(cuò),可以轉(zhuǎn)發(fā)給更多的朋友!關(guān)于本文的示例工程,可以在關(guān)注本公眾號(hào)后,回復(fù)“maven”后臺(tái)會(huì)自動(dòng)回復(fù)下載鏈接!



          —————END—————


          參考鏈接:

          https://www.petrikainulainen.net/programming/maven/integration-testing-with-maven/

          瀏覽 97
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲黄色电影网址 | 国产精品午夜未成人免费观看 | 国外成人 性视频免费 | 在线综合国产 | 欧美三级片一区二区 |